函数参数的求值顺序是什么?
What is the evaluation order of function arguments?
我正在使用这个版本的 Go:
$ go version
go version go1.18 windows/amd64
struct A
只有一个字段和B
有两个或以上字段时结果不一样,只有参数类型是interface时才会出现这种情况。
我不确定这是否是一个错误:
package main
import (
"fmt"
)
func main() {
a := A{}
m("A", a, SetAI(&a))
b := B{}
m("B", b, SetBI(&b))
}
type A struct {
I int
S string
}
type B struct {
I int
}
func SetAI(a *A) A {
a.I = 10
return *a
}
func SetBI(b *B) B {
b.I = 10
return *b
}
func m(name string, arg1, arg2 interface{}) {
fmt.Println(name+":", arg1, arg2)
}
我期望这个输出:
A: {10} {10}
B: {10} {10}
相反,我得到了这个:
A: {0 } {10 }
B: {10} {10}
混淆和不同输出的根源是参数的评估顺序。
看看你的例子:
m("A", a, SetAI(&a))
这是一个函数call, the function value and arguments are evaluated in the usual order:
Otherwise, when evaluating the operands of an expression, assignment, or return statement, all function calls, method calls, and communication operations are evaluated in lexical left-to-right order.
For example, in the (function-local) assignment
y[f()], ok = g(h(), i()+x[j()], <-c), k()
the function calls and communication happen in the order f()
, h()
, i()
, j()
, <-c
, g()
, and k()
. However, the order of those events compared to the evaluation and indexing of x
and the evaluation of y
is not specified.
所以基本上规范只保证函数调用和通信
操作发生在 left-to-right.
您的调用有参数 "A"
、a
和 SetAI(&a)
。无法保证第二个参数 a
是否在 &a
参数传递给 SetAI()
之前被评估,这非常重要,因为 SetAI()
修改了 a
。由于无法保证顺序,您不能依赖哪个顺序会先评估,这两个顺序均符合规范。
如果在 之前通过复制结构 来显式求值,您会得到相同的结果:
a := A{}
aCopy := a
m("A", aCopy, SetAI(&a))
b := B{}
bCopy := b
m("B", bCopy, SetBI(&b))
这将输出(在 Go Playground 上尝试):
A: {0 } {10 }
B: {0} {10}
或者,如果您希望首先评估函数调用:
a := A{}
ap := SetAI(&a)
m("A", a, ap)
b := B{}
bp := SetBI(&b)
m("B", b, bp)
这将为每种情况输出 10
(在 Go Playground 上尝试这个):
A: {10 } {10 }
B: {10} {10}
我正在使用这个版本的 Go:
$ go version
go version go1.18 windows/amd64
struct A
只有一个字段和B
有两个或以上字段时结果不一样,只有参数类型是interface时才会出现这种情况。
我不确定这是否是一个错误:
package main
import (
"fmt"
)
func main() {
a := A{}
m("A", a, SetAI(&a))
b := B{}
m("B", b, SetBI(&b))
}
type A struct {
I int
S string
}
type B struct {
I int
}
func SetAI(a *A) A {
a.I = 10
return *a
}
func SetBI(b *B) B {
b.I = 10
return *b
}
func m(name string, arg1, arg2 interface{}) {
fmt.Println(name+":", arg1, arg2)
}
我期望这个输出:
A: {10} {10}
B: {10} {10}
相反,我得到了这个:
A: {0 } {10 }
B: {10} {10}
混淆和不同输出的根源是参数的评估顺序。
看看你的例子:
m("A", a, SetAI(&a))
这是一个函数call, the function value and arguments are evaluated in the usual order:
Otherwise, when evaluating the operands of an expression, assignment, or return statement, all function calls, method calls, and communication operations are evaluated in lexical left-to-right order. For example, in the (function-local) assignment
y[f()], ok = g(h(), i()+x[j()], <-c), k()
the function calls and communication happen in the order
f()
,h()
,i()
,j()
,<-c
,g()
, andk()
. However, the order of those events compared to the evaluation and indexing ofx
and the evaluation ofy
is not specified.
所以基本上规范只保证函数调用和通信 操作发生在 left-to-right.
您的调用有参数 "A"
、a
和 SetAI(&a)
。无法保证第二个参数 a
是否在 &a
参数传递给 SetAI()
之前被评估,这非常重要,因为 SetAI()
修改了 a
。由于无法保证顺序,您不能依赖哪个顺序会先评估,这两个顺序均符合规范。
如果在 之前通过复制结构 来显式求值,您会得到相同的结果:
a := A{}
aCopy := a
m("A", aCopy, SetAI(&a))
b := B{}
bCopy := b
m("B", bCopy, SetBI(&b))
这将输出(在 Go Playground 上尝试):
A: {0 } {10 }
B: {0} {10}
或者,如果您希望首先评估函数调用:
a := A{}
ap := SetAI(&a)
m("A", a, ap)
b := B{}
bp := SetBI(&b)
m("B", b, bp)
这将为每种情况输出 10
(在 Go Playground 上尝试这个):
A: {10 } {10 }
B: {10} {10}