Go函数支持多返回值,声明为func() (T1, T2),调用用a, b := f();需检查ok或error再使用返回值,避免裸return与defer交互问题。

Go 函数多返回值的声明和调用写法
Go 语言原生支持函数返回多个值,不需要包装成结构体或切片。声明时直接在 func 尾部用括号列出返回类型,例如 func name() (int, string);调用时可用多个变量一次性接收,如 a, b := name()。
常见错误是只用一个变量接收多返回值,导致编译报错:multiple-value name() in single-value context。
- 返回值类型列表必须用括号包裹,即使只有一个类型也要加括号(如
()string) - 命名返回值(如
func f() (x int, y string))会自动声明同名变量,return可不带参数(即“裸 return”),但需谨慎——容易掩盖未显式赋值的逻辑分支 - 如果只关心部分返回值,用下划线
_忽略,例如_, err := os.Open("x")
error 处理是多返回值最典型的应用场景
Go 标准库几乎全部 I/O 和解析类函数都采用 (T, error) 模式,比如 os.ReadFile、strconv.Atoi、json.Unmarshal。这种设计让错误无法被静默忽略。
不要写成 if err != nil { ... } else { success logic } 嵌套过深;推荐提前返回:
立即学习“go语言免费学习笔记(深入)”;
data, err := os.ReadFile("config.json")
if err != nil {
return err
}
// 后续逻辑直接用 data,无需 else 包裹
- 自定义函数也应优先使用
(result, error)模式,而非 panic 或全局错误变量 - 多个 error 类型?可定义自定义 error 类型并用
errors.Is判断,而不是靠字符串匹配 - 注意:
defer中的命名返回值变量在函数退出前才确定最终值,裸return可能导致意料外的行为
如何安全地解构多返回值(尤其带 error 的情况)
不能假设所有返回值都有效。例如 strings.Cut 返回 (before, after string, found bool),必须先检查 found 再使用 before 和 after;又如 map[key] 返回 value, ok,ok 为 false 时 value 是零值,不可直接用于业务逻辑。
- 避免链式调用多返回函数,如
f(g())—— 无法单独处理中间函数的error - 需要同时检查多个返回值时,用短变量声明 + if 合并判断:
if v, ok := m[k]; ok { ... } - 在循环中处理多返回值(如
range的key, value)时,若只用一个变量,第二个值会被静默丢弃(不是报错),容易引发逻辑 bug
命名返回值带来的隐蔽风险
命名返回值看似方便,但会让函数体内部变量作用域和生命周期变复杂。尤其是配合 defer 使用时,defer 表达式捕获的是返回值变量的引用,而非当时值。
func bad() (err error) {
defer func() {
if err == nil {
err = fmt.Errorf("deferred error")
}
}()
return nil // 实际返回的是 "deferred error"
}
- 裸
return在有 defer 时行为难预测,建议显式写出所有返回值 - 命名返回值会增加函数签名与实现之间的耦合,重构时容易漏改变量名
- 导出函数尽量避免命名返回值,除非语义非常清晰(如
func MinMax([]int) (min, max int))










