Go 语言中,defer
语句中可以修改函数返回变量,导致返回值被修改。请看下面各种情况:
package main
import "fmt"
// 返回不受 defer 影响,返回 0
func f0() int {
var i int
defer func() { i++ }()
return i
}
// 返回被 defer 修改后的值 1
func f1() (i int) {
defer func() { i++ }()
return
}
// 返回引用类型受 defer 影响,返回 [2]
func f2() []int {
var i = []int{1}
defer func() { i[0]++ }()
return i
}
// 指明返回变量 a ,但实际返回的还是 i 。经过 defer 修改后返回 3
func f3() (i int) {
i = 100 // 在 return 时被重新赋值
a := 2
defer func() { i++ }()
return a
}
// i 在返回前赋值为 3 但不返回,经过 defer 修改,返回 4
func f4() (i int) {
defer func() { i++ }()
return 3
}
func main() {
fmt.Println(f0(), f1(), f2(), f3(), f4()) // 输出:0 1 [2] 3 4
}
站内有篇对这一现象说明的文章,经过实际测试( Go 1.22 )得到下面理解:
return
时,这个隐藏变量被赋予 i
的字面量值。defer
语句中修改 i
的值不会影响到隐藏返回变量,所以最终返回 0
。当然,如果 i
的类型为引用型(例如切片),那么赋值给隐藏返回变量时,是引用传递,defer
语句中的修改依然会体现到返回值上。i
赋值,如果 return
后带有值(或变量)则赋给 i
;然后执行 defer
语句,里面可能修改 i
的值;最后将 i
的最终值返回。经过上面解释,正常 return
语句和裸 return
语句在逻辑上达成了一致。
这里不讨论为什么具名返回值被设计成这样的,我的疑惑是:
如果没有使用场景,我便直接将其理解为「陷阱」。就同 for
循环初始化变量只初始化一次一样,小心别用,等待官方改进就是了。
经验尚浅,还望大佬们不吝赐教!
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.