Go 语言中延迟语句修改返回值算陷阱还是特性?

81 天前
 assassing

0x00 现象

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
}

0x01 解释

站内有篇对这一现象说明的文章,经过实际测试( Go 1.22 )得到下面理解:

经过上面解释,正常 return 语句和裸 return 语句在逻辑上达成了一致。

0x02 疑惑

这里不讨论为什么具名返回值被设计成这样的,我的疑惑是:

如果没有使用场景,我便直接将其理解为「陷阱」。就同 for 循环初始化变量只初始化一次一样,小心别用,等待官方改进就是了。

经验尚浅,还望大佬们不吝赐教!

2196 次点击
所在节点    程序员
9 条回复
nagisaushio
81 天前
场景:defer 中捕捉到 panic ,转成 err 返回
maocat
81 天前
代码出现了 panic ,我需要转成 err 返回
assassing
81 天前
@nagisaushio #1 准,算一例
povsister
81 天前
一种变相的 catch all 操作
除了 panic 转 err 外,业务上常见对于数据进行返回前检查
将检查条件写入 defer 内,这样可以在一大坨可能好几个月后看不懂的逻辑里少写点 return 赋值。
assassing
81 天前
@povsister #4 可是可以,但那样就不好写测试代码了
rekulas
80 天前
算特性吧, 会这样写的大多应该了解这样做会产生什么影响,不至于引起意外 bug
我很喜欢这特性,当做函数的析构逻辑来用,例如某些复杂方法内部可能包含多个判断,可能会涉及提前返回,不同的返回可能关联了不同的退出逻辑,我就可以在 defer 里统一处理,而不用每个 return 都去调用或者判断
assassing
80 天前
@rekulas #6 请问怎么理解「不同的返回可能关联了不同的退出逻辑」?这是使用 defer 修改返回值的关键。我理解的这种情况,需要后续单独使用一个函数来处理返回值,除非在 defer 中处理能获得相当大的便利性
rekulas
80 天前
@assassing 后续单独使用一个函数来处理返回值
自然也是可以的,但如果你的逻辑依赖的数据是函数内部变量,那你可以选择定义内部函数然后每个退出都调用一次,总归没有 defer 方便,而且可能遗漏导致 bug ,另外有些场景下可能需要对返回值进行二次处理,用 defer 也更方便,不容易出 bug
举个例子,一个典型场景函数内部开启事务,在 defer 中 commit 就可以避免出现事务未提交阻塞数据库的情况,现在很多 orm 也用这个原理封装了事务函数,尽最大可能降低了 bug 产生率
assassing
80 天前
@rekulas #8 理解了,谢谢!

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1067936

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX