Go多值返回中表达式求值顺序是否符合语言规范?
Go多值返回的求值顺序问题
问题背景
《Go语言规范》中明确给出示例:切片和map赋值时,原语与函数调用的求值顺序未被指定,但规范未提及多值返回场景,因此存在疑问:该规则是否适用于多值返回的表达式?
规范示例代码:
a := 1 f := func() int { a++; return a } x := []int{a, f()} // x可能为[1, 2]或[2, 2]:a与f()的求值顺序未指定 m := map[int]int{a: 1, a: 2} // m可能为{2: 1}或{2: 2}:两个map赋值的求值顺序未指定 n := map[int]int{a: f()} // n可能为{2: 3}或{3: 3}:键与值的求值顺序未指定
核心疑问
疑问1:多值返回的表达式求值顺序是否未指定?
如下函数的返回结果是否存在两种可能?
func run() (int, int) { a := 1 f := func() int { a++; return a } return a, f() // 始终返回1,2?始终返回2,2?还是两种都可能? }
疑问2:依赖求值顺序的代码是否存在风险?
若求值顺序未指定,如下代码可能因编译器更新在未来出现问题:
func CountRows(ctx context.Context, db *pgxpool.Pool) (int, error) { row := db.QueryRow(ctx, "SELECT COUNT(*)") var count int return count, row.Scan(&count) }
原始问题
不清楚Go语言规范是否明确说明函数返回值是逐个求值返回,还是所有表达式求值完成后统一返回。如下代码是否能保证始终输出10 <nil>,还是可能输出0 <nil>?
package main import "fmt" func main() { fmt.Println(run()) // Output: 10 <nil> } func run() (int, error) { var i int return i, inc(&i) } func inc(i *int) error { *i = *i + 10 return nil }
结论
根据Go语言规范及相关讨论:
- 多值返回语句中,各返回表达式的求值顺序未被规范指定,编译器可以按任意顺序求值这些表达式。
- 第一个
run()函数的返回结果可能是1,2也可能是2,2,两种情况均符合规范要求。 - 原始问题中的代码,当前Go playground输出
10 <nil>是因为编译器先求值inc(&i)再求值i,但规范不保证这个顺序,未来编译器更新后可能出现0 <nil>的结果。 - 类似
CountRows的代码存在风险,因为row.Scan(&count)修改count后,count的求值顺序不确定,可能返回未被修改的初始值0。
依赖未指定的求值顺序编写代码是不安全的,应避免这种写法,确保代码逻辑不依赖求值顺序。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

