函数参数的评估顺序是什么?

huangapple go评论72阅读模式
英文:

What is the evaluation order of function arguments?

问题

我正在使用以下版本的Go:

$ go version
go version go1.18 windows/amd64

当结构体A只有一个字段而B有两个或更多字段时,且参数类型为接口时,结果会有所不同。

我不确定这是否是一个 bug:

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}
英文:

I'm using this version of Go:

$ go version
go version go1.18 windows/amd64

The results are different when struct A has only one field and B has two or above fields, and it just happens when parameter type is interface.

I'm not sure if that's a bug:

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)
}

I expected this output:

A: {10} {10}
B: {10} {10}

Instead I got this:

A: {0 } {10 }
B: {10} {10}

答案1

得分: 7

混淆和不同的输出源于参数的评估顺序。

看看你的例子:

m("A", a, SetAI(&a))

这是一个函数调用,函数值和参数按照通常的顺序进行评估:

> 否则,在评估表达式、赋值语句或返回语句的操作数时,所有函数调用、方法调用和通信操作按照从左到右的词法顺序进行评估。
> 例如,在(函数局部的)赋值语句中
>
> y[f()], ok = g(h(), i()+x[j()], <-c), k()
>
> 函数调用和通信操作按照顺序 f()h()i()j()<-cg()k() 发生。然而,这些事件与 x 的评估和索引以及 y 的评估的顺序没有指定。

因此,规范只保证函数调用和通信操作按照从左到右的顺序发生。

你的调用有参数 "A"aSetAI(&a)。无法保证第二个参数 a 在传递给 SetAI()&a 参数之前进行评估,这非常重要,因为 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}
英文:

The source of confusion and different output is the order of evaluation of the arguments.

Look at your example:

m(&quot;A&quot;, a, SetAI(&amp;a))

This is a function 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(), &lt;-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.

So basically the spec only guarantees that function calls and communication
ops happen from left-to-right.

Your call has arguments &quot;A&quot;, a and SetAI(&amp;a). There is no guarantee if the second argument a is evaluated before the &amp;a param passed to SetAI(), and this very much matters because SetAI() modifies a. Since the order is not guaranteed, you can't rely on which will be evaluated first, both order is valid by the spec.

If you make the evaluation explicit by doing a copy of the struct before, you get the same result:

a := A{}
aCopy := a
m(&quot;A&quot;, aCopy, SetAI(&amp;a))
b := B{}
bCopy := b
m(&quot;B&quot;, bCopy, SetBI(&amp;b))

This will output (try it on the Go Playground):

A: {0 } {10 }
B: {0} {10}

Or if you want the function call to be evaluated first:

a := A{}
ap := SetAI(&amp;a)
m(&quot;A&quot;, a, ap)
b := B{}
bp := SetBI(&amp;b)
m(&quot;B&quot;, b, bp)

This will output 10 for each cases (try this one on the Go Playground):

A: {10 } {10 }
B: {10} {10}

huangapple
  • 本文由 发表于 2022年4月6日 15:05:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/71762407.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定