You're confusing calling a method on a pointer receiver with keeping a pointer to a value. That is, there is no inherent connection between calling SetName
defined on a pointer receiver with the requirement to store a pointer to a memory holding a value of type Person
vs storing Person
directly.
When you have the declaration
var pers Person
and then call
pers.SetName("foo")
the compiler takes the address of the memory block occupied by the
variable pers
—just like if you were doing that by hand through applying
the &
operator to pers
, and passes that address to the SetName
function, so the call ends up being done like
(&pers).SetName("foo")
or, to say it differently, the SetName
's receiver will be
&pers
—that is, the address of pers
.
Now there's nothing preventing you from taking the address of pers
and storing it elsewhere:
var ptr *Person = &pers
(you usually do not write such Java-style stuttering code but
I'm doing this for extra clarity), and now you're able to call
SetName
right on the ptr
value:
ptr.SetName("bar")
Now the compiler will directly use the value stored in the variable ptr
to pass it as the method's receiver.
The final bit of this "puzzle" is that when you use
the special syntax provided by Go—applying the address-taking operator &
directly to a literal,—the compiler does something like this:
var __pers Person = Person{
// The contents of your literal
}
var dave = &__pers
…and makes the __pers
variable inaccessible by name (since it naturally
has no name).
As you can see, this is no different from getting the pointer
to a Person
value's memory by hand.
You might want to consult this FAQ entry to gain more understanding
of how the method sets on a type T
and *T
relate, and why the latter
always includes the former but not vice-versa.
Also read this.
Update: to explain the difference in how fmt.Println
formats and outputs
the values of type *int
and *Person
.
- The former is printed as the base-16 string representation
of an address of a memory location containing that
int
value.
- The latter is rendered using a string representation resembling
Go's syntax for taking a pointer to a literal:
&{}
.
This behaviour is as documented: fmt.Print
and fmt.Println
use the so-called default formatting rules for the arguments they
are passed, and these rules depend on the types of the arguments.
Let's first consult the documentation on fmt.Println
(run go doc fmt.Println
):
func Println(a ...interface{}) (n int, err error)
Println
formats using the default formats for its operands
and writes to standard output.
Spaces are always added between operands and a newline is appended.
It returns the number of bytes written and any write error encountered.
(Emphasis mine.)
Now let's turn to the documentation of the fmt
package itself
(run go doc fmt
):
<…>
Printing
The verbs:
General:
<…>
The default format for %v
is:
bool: %t
int, int8 etc.: %d
uint, uint8 etc.: %d, %#x if printed with %#v
float32, complex64, etc: %g
string: %s
chan: %p
pointer: %p
For compound objects, the elements are printed using these rules,
recursively, laid out like this:
struct: {field0 field1 ...}
array, slice: [elem0 elem1 ...]
maps: map[key1:value1 key2:value2]
pointer to above: &{}, &[], &map[]
A *Person
is a pointer to a struct type, so it's rendered as
&{name}
where name
is the contents of the name field; say, if it was assigned
the string "Dave", the output would be &{dave}
.