The easiest way to think about both functions is that in the t2
function, you're changing a field of a struct, using a pointer to the underlying struct. In the t
function, you're changing the entire underlying object (the int).
In reality, the fact that you can write p.X
is actually just a nicety. In languages like C, you could only use p.X
if you were operating on a non-pointer variable. For pointers, you had to either use p->X
denoting you were accessing a field using indirection, or indeed dereference the pointer ((*p).X
).
Internally, go still does the same thing, it just allows you to omit the explicit dereferencing, and it eliminates the need for an indirection operator.
Both functions, however, are not equivalent. Point
is a struct, with one or more fields. The other type is *int
, an int is a single scalar value. To make t2
equivalent (and reassign the entire underlying object), you'll have to change the code to be identical to what you have to do in case of *int
:
func t2(p *Point) {
*p = Point{
X: 200,
Y: p.Y,
}
}
As per comment below: the TL;DR version is that you don't have to explicitly dereference a pointer to a struct type if you access one of its fields. You had to do that in C/C++, but the go compiler takes care of that for you. It works out you're using a variable that is a pointer type and compiles p.X
in the same way that a C compiler would compile p->X
. Therefore, you don't need to dereference p
explicitly.
You would still have to write *p.X
if you declared Point
as:
type Point struct {
X *int
}
Because the expression p.X
evaluates to an operand of type *int
, which needs to be treated accordingly.