返回多个值
在圆括号中列出返回值类型。返回时用逗号分隔各个值,接收时也一样。
内置的 min 和 max 从 Go 1.21 起就有了。调用者必须接收所有返回值:x := minMax(nums) 无法编译,会报错 assignment mismatch: 1 variable but minMax returns 2 values。
(value, error) 约定
返回两个值最常见的原因是报告失败。按照约定,错误放在最后;当它不为 nil 时,其他返回值都应视为没有意义。
输出:
port: 8080
error: port "http": strconv.Atoi: parsing "http": invalid syntax
error: port out of range
失败时,其他所有返回值都返回零值(0、""、nil)。完整的模式,包括包装错误和检查错误,见错误处理页面。
comma-ok 惯用法
有几种内置操作会返回一个可选的第二个 bool 值,表示第一个值是否真实有效。这个变量按惯例命名为 ok。
这三种形式(map 查找、类型断言、通道接收)很特殊:它们返回一个值还是两个值,取决于你赋给几个变量。你自己的函数做不到这一点。声明了两个返回值的函数,总是返回两个值。
空白标识符
_ 接收一个值,然后把它丢掉。用它来接收你不需要的返回值:
_, err := fmt.Sscan(input, &n)
key, _ := parseLine(line)
用 _ 忽略错误能编译通过,所以只在你确定调用不会失败,或者失败也无所谓时才这样做。例如,strings.Builder.WriteString 总是返回 nil 错误;但写入后关闭文件就不是这样了。
命名返回值
返回值参数可以有名字。它们在函数开头声明,初始化为各自的零值,一个不带值的 return 会返回它们当前保存的值。
这些名字会出现在 go doc 的输出中,当两个返回值类型相同时尤其有用:(quotient, remainder int) 比 (int, int) 传达的信息更多。
在长函数中,裸 return 很难读,因为读者得往回翻才能知道返回值里装的是什么。通常的建议是:当名字能起到说明作用时就给返回值命名,但除非函数只有几行,否则仍然显式写出 return quotient, remainder。
命名返回值与 defer
defer 的函数在 return 语句设置好返回值之后、调用者收到它们之前运行。有了命名返回值,defer 的函数就可以读取和修改它们。借助这一点,你可以给函数返回的任何错误加上上下文,或者把 panic 转换成错误。
这会先打印 load : empty name,再打印 <nil>。return errors.New(...) 先把值赋给 err,然后 defer 的函数再包装它。更多内容见 defer。
遮蔽命名返回值
命名返回值位于函数的最外层作用域。在嵌套块中使用 := 会创建一个同名的新变量,而裸 return 返回的是外层那个。
func find() (n int, err error) {
if true {
n, err := compute() // new n and err, shadowing the results
_ = n
_ = err
}
return // returns 0, nil
}
编译器能发现最糟糕的情况:在返回值被遮蔽的块内写裸 return,会报错 result parameter err not in scope at return。但它发现不了上面这种 return 位于块外的写法。当你指的是返回值变量时,用 = 而不是 :=。
常见错误
- 在 err 不为 nil 时使用返回的值。 先检查
err。函数在返回错误的同时,完全可以返回无意义的值。 - 把带类型的 nil 指针作为 error 返回。
var e *MyErr; return e返回的是一个非 nil 的error。成功时请返回字面量nil。 - 返回值太多。 三个或更多含义各异的返回值很难使用。改为返回一个结构体。
常见问题
Go 语言的函数怎么返回多个值?
在圆括号中列出返回值类型,返回时用逗号分隔各个值:func minMax(xs []int) (int, int) { ...; return lo, hi }。调用者一次性接收所有返回值:lo, hi := minMax(nums)。
Go 语言怎么忽略其中一个返回值?
把它赋给空白标识符 _:_, err := strconv.Atoi(s) 或 n, _ := strconv.Atoi(s)。你不能完全省略某个值:左边的变量个数必须和返回值个数一致。
Go 中的命名返回值是什么?
就是带名字的返回值参数,比如 func split(n int) (half, rest int)。它们从各自的零值开始,可以在函数体中赋值,一个不带值的 return 会返回它们当前的值。它们还能让 defer 的函数修改调用者最终收到的结果。
Go 中应该使用裸返回吗?
只在短函数中使用。在长函数中,一个不带值的 return 会让人看不出返回的是什么,也容易引发变量遮蔽的 bug。Go 风格指南建议:为了文档作用可以给返回值命名,但只要函数超过几行,就显式写出返回的值。