函数参数的求值顺序是什么?

eas*_*asy 2 evaluation arguments go order-of-execution

我正在使用这个版本的 Go:

$ go version
go version go1.18 windows/amd64
Run Code Online (Sandbox Code Playgroud)

A当 struct只有一个字段时,结果会有所不同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)
}
Run Code Online (Sandbox Code Playgroud)

我期望这个输出:

A: {10} {10}
B: {10} {10}
Run Code Online (Sandbox Code Playgroud)

相反,我得到了这个:

A: {0 } {10 }
B: {10} {10}
Run Code Online (Sandbox Code Playgroud)

icz*_*cza 7

混乱和不同输出的根源是参数的评估顺序。

看看你的例子:

m("A", a, SetAI(&a))
Run Code Online (Sandbox Code Playgroud)

这是一个函数调用,函数值和参数按通常的顺序求值

否则,在计算表达式、赋值或return 语句的操作数时,所有函数调用、方法调用和通信操作都按词法从左到右的顺序进行计算。例如,在(函数局部)赋值中

y[f()], ok = g(h(), i()+x[j()], <-c), k()
Run Code Online (Sandbox Code Playgroud)

函数调用和通信按照f()h()i()j()<-cg()和的顺序发生k()但是,未指定这些事件与评估和索引以及评估相比的顺序。xy

所以基本上规范只保证函数调用和通信操作从左到右发生。

您的调用有参数"A"a并且SetAI(&a)。无法保证第二个参数是否在传递给 parama之前求值,这非常重要,因为会修改。由于顺序不受保证,因此您不能依赖首先评估哪个顺序,两个顺序均符合规范。&aSetAI()SetAI()a

如果通过在 before 之前复制结构体来明确求值,则会得到相同的结果:

a := A{}
aCopy := a
m("A", aCopy, SetAI(&a))
b := B{}
bCopy := b
m("B", bCopy, SetBI(&b))
Run Code Online (Sandbox Code Playgroud)

这将输出(在Go Playground上尝试):

A: {0 } {10 }
B: {0} {10}
Run Code Online (Sandbox Code Playgroud)

或者,如果您希望首先评估函数调用:

a := A{}
ap := SetAI(&a)
m("A", a, ap)
b := B{}
bp := SetBI(&b)
m("B", b, bp)
Run Code Online (Sandbox Code Playgroud)

这将为每种情况输出(在Go Playground10上尝试这个):

A: {10 } {10 }
B: {10} {10}
Run Code Online (Sandbox Code Playgroud)