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}
CodePudding user response:
The source of confusion and different output is the order of evaluation of the arguments.
Look at your example:
m("A", a, SetAI(&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()
,<-c
,g()
, andk()
. However, the order of those events compared to the evaluation and indexing ofx
and the evaluation ofy
is not specified.
So basically the spec only guarantees that function calls and communication ops happen from left-to-right.
Your call has arguments "A"
, a
and SetAI(&a)
. There is no guarantee if the second argument a
is evaluated before the &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("A", aCopy, SetAI(&a))
b := B{}
bCopy := b
m("B", bCopy, SetBI(&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(&a)
m("A", a, ap)
b := B{}
bp := SetBI(&b)
m("B", b, bp)
This will output 10
for each cases (try this one on the Go Playground):
A: {10 } {10 }
B: {10} {10}