A bit confused by the order of pointer receiver calls

177 views
Skip to first unread message

ran...@gmail.com

unread,
Jul 17, 2026, 9:44:32 AM (4 days ago) Jul 17
to golang-nuts
Hi all,

So, given following code:

type Num int

func (n *Num) add(s int) Num {
defer func() { *n -= Num(s) }()
return *n + Num(s)
}

func (n Num) get() Num {
return n
}

func TestNumAdd(t *testing.T) {
n := Num(10)
a, b, c := n, n.add(1), n.add(2)
// a, b, c := n.get(), n.add(1).get(), n.add(2).get()
fmt.Println(a, b, c)
}

What do you think the `TestNumAdd` function will output?

The most human answer would probably be `10 11 11`. But the output will actually be `7 11 11` as of 1.26.

If you want to play it yourself, the link: https://go.dev/play/p/oC01iNj-47x

Changing the `TestNumAdd` function to:

func TestNumAdd(t *testing.T) {
n := Num(10)
// a, b, c := n, n.add(1), n.add(2)
a, b, c := n.get(), n.add(1).get(), n.add(2).get()
fmt.Println(a, b, c)
}

then it will actually give you the expected output `10 11 11`.

PS. I also asked Gemini the same question above, and it was also confused. See:
截图 2026-07-17 21-29-13.png

Pepper Lebeck-Jobe

unread,
Jul 17, 2026, 10:14:08 AM (4 days ago) Jul 17
to ran...@gmail.com, golang-nuts
I'm not sure why anyone would write this code. But, there isn't really anything surprising by the evaluation once you understand that the evaluation of the multi-assignment line.

Specifically, everything on the right-hand side is going to be evaluated before any of the resulting calls are stored in the left-hand variables.

So, if you wrote it as:

a := n
b := n.add(1)
c := n.add(2)

You'll also get what you call "The most human answer" although, I find that characterization of the behavior to be quite loaded.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/d98126a7-1529-43ed-af1a-d0dd15e2a140n%40googlegroups.com.

Marvin Renich

unread,
Jul 17, 2026, 11:57:10 AM (4 days ago) Jul 17
to golan...@googlegroups.com
* ran...@gmail.com <ran...@gmail.com> [260717 09:45]:
> Hi all,
>
> So, given following code:
[snip]
> func TestNumAdd(t *testing.T) {
> n := Num(10)
> a, b, c := n, n.add(1), n.add(2)
> // a, b, c := n.get(), n.add(1).get(), n.add(2).get()
> fmt.Println(a, b, c)
> }
>
> What do you think the `TestNumAdd` function will output?

The Go Language Specification is very explicit about this:

https://go.dev/ref/spec#Order_of_evaluation

See the explanation under the first example:

> However, the order of those events compared to the evaluation and
> indexing of x and the evaluation of y and z is not specified,
> except as required lexically.

So, all function calls (n.add()) happen in left-to-right order, but when
they are evaluated relative to the evaluation of simple variables (n in
this case) is explicitly unspecified.

> PS. I also asked Gemini the same question above, and it was also confused.

What did you expect from an LLM? It only knows what other people have
told it, and even then it has a hard time telling when it has been told
the truth or a falsehood.

...Marvin

ran...@gmail.com

unread,
Jul 18, 2026, 12:17:03 AM (3 days ago) Jul 18
to golang-nuts
Hi, thanks for the explanations.

I think the example in the doc:

a := 1
f := func() int { a++; return a }
x := []int{a, f()} // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified

is exactly why I'm so confused. Maybe the evaluation order of function calls and values should be specified under a single rule somehow? Before this becomes a widely spread most-ask job interview question, perhaps?

robert engels

unread,
Jul 18, 2026, 12:37:57 AM (3 days ago) Jul 18
to ran...@gmail.com, golang-nuts
It’s very C++ like - good to see the specter is still alive. Isn’t UB great?

On Jul 17, 2026, at 11:17 PM, ran...@gmail.com <ran...@gmail.com> wrote:


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Ian Lance Taylor

unread,
Jul 18, 2026, 6:53:22 PM (2 days ago) Jul 18
to ran...@gmail.com, golang-nuts
On Fri, Jul 17, 2026 at 9:17 PM ran...@gmail.com <ran...@gmail.com> wrote:
>
> Hi, thanks for the explanations.
>
> I think the example in the doc:
>
> a := 1
> f := func() int { a++; return a }
> x := []int{a, f()} // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified
>
> is exactly why I'm so confused. Maybe the evaluation order of function calls and values should be specified under a single rule somehow? Before this becomes a widely spread most-ask job interview question, perhaps?

We could fully define the evaluation order of all operations in Go. So
far we have not done so. The main argument against it is runtime
efficiency: if the evaluation order is fully defined, the compiler is
more constrained, and there is less flexibility in generating optimal
code.

That said, there are other areas where Go has decided to specify the
language in ways that tend to make code run less efficiently. There
can be a tradeoff between making the code readable and making the code
run fast. Go tends to prefer to making the code readable.

With that in mind, it worth observing that code like the example above
is not clear. The variable a is being read by the slice expression,
and it is also being modified by the slice expression. That is
confusing. This is not a case in which there is a trade off between
readability and efficiency. It's a case in which the code is not
readable. If I saw code like this in a code review, I would reject it.

I've personally felt for a long time that Go should make it an error
for a variable to be both read and written in a single expression.
Unfortunately that is difficult to implement in the fully general
case.

Ian

robert engels

unread,
Jul 18, 2026, 7:09:01 PM (2 days ago) Jul 18
to Ian Lance Taylor, ran...@gmail.com, golang-nuts
I don’t disagree at all. And I feel the same way - there’s no reason to write code like that and it certainly shouldn’t pass review. I think allowing efficiency for the compiler is orthogonal. Fully specify the language so that it can be efficient and disallow the constructs that would hinder that. UB is like a “your mileage may vary” claim - it’s not very useful and hinders agreement.

> On Jul 18, 2026, at 5:53 PM, Ian Lance Taylor <ia...@golang.org> wrote:
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/CAOyqgcXMEwUDMgv4TUathHHhmd6AJ--ndeJsfNGKhTkG1Ds0OQ%40mail.gmail.com.
Reply all
Reply to author
Forward
0 new messages