如何在 Golang 中将 nil 作为可选参数传递给函数?

21

在Golang中,http.NewRequest的规范如下:

func NewRequest(method, urlStr string, body io.Reader) (*Request, error)

然而,如果我不想将 body 传递给 io.Reader 对象,我可以将选项 body 设置为 nil,如下所示:

req, err := http.NewRequest("GET", "http://www.blahblah.org", nil)

我该如何在我的代码中实现这个功能?我有一个函数,想要传递一个可选的字符串值,以便可以通过 API 结果进行分页。但是,如果我向字符串输入传递 nil,就会出现以下情况:

./snippets.go:32: cannot convert nil to type string

我的函数参数看起来像这样:

func getChallenges(after string) ([]challenge, string, error)
6个回答

14

在其他语言通常理解的概念中,Go语言没有“可选”参数;nil只是接口(在本例中为io.Reader)的零值。

字符串的等效零值是一个空字符串:

getChallenges("")

如果你想接受0个或多个相同类型的参数,可以使用可变参数语法:

func getChallenges(after ...string) ([]challenge, string, error)

空字符串 "" 是我之前使用的,不过我会实现可变语法。我猜这跟 Python 中的 *arg/**kwargs 类似。干杯! - user5076297

12
你可以修改你的函数接收指针值,像这样:func getChallenges(after *string) ([]challenge,string,error),然后你可以将nil作为参数传递给它。但不要忘记在解引用之前在函数内部检查after是否为nil值,否则你会得到一个nil指针异常:
func getChallenges(after *string) ([]challenge, string, error) {
    if after == nil {
        // No value specified
    } else {
        fmt.Printf("After: %s\n", *after) // Note pointer dereferencing with "*"
    }
    // ...
}

另一种选项:

只需使用两个函数:

func getChallenges(after string) {}

func getAllChallenges() {
    return getChallenges(/* some default value here */)
}

4
将参数定义为字符串的一个巨大缺陷是你无法获取字符串字面量的地址,也不会自动将字符串打包。 - Chris Harrington
1
“只需添加另一个函数”的论点可能导致需要处理多个排列的大量方法爆炸,需要承担决定这些方法有意义、可发现和易记名称的负担,并增加了学习使用更多方法与较少方法的包的负担。#jmtcw - MikeSchinkel

4

您可以使用省略运算符来发送可选参数...不要在可选参数中传递任何内容并检查参数的长度。这应该可以解决您的问题。

func foo(params ...int) {
   fmt.Println(len(params))
}

func main() {
    foo()
    foo(1)
    foo(1,2,3)
}

0
也许可以用一个 struct 来包装它?
type NilableString struct {
    value string;
}

0

以下是如何使这些函数接受 nil 和值。

req, err := http.NewRequest(method, path, reader)

reader 应该接受 io.Readernil

packege io

type Reader struct {
  .....
}

func (r Reader) getData() []byte {
  ......
}

type ReaderInterface interface {
  getDate() []byte
}

package http

import "io"

func NewRequest(method string, path string, reader ReaderInterface) {
  if (reader != nil) {
    data := reader.getData()
  }
  .....
}

请注意,NewRequest预期的类型是ReaderInterface而不是Reader本身。像上面那样具有GetData函数或nil的任何类型都将被接受。在内部,您可以检查读取器是否不为零。

-1

你可以使用反射。

实际上,io.Reader 是一个接口。

因此,你可以定义如下签名:func getChallenges(after interface{}) ([]challenge, string, error)

interface{} 是一个空接口,也就是任何类型的接口。

但我建议你使用语法 args... 来传递切片,参考 fmt.Printf 的实现用法,因为如果你不传递字符串,切片长度为 0,这将避免使用反射,我认为这太重了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接