There isn't one correct way of doing this. It's important to understand the differences between the two and apply each where appropriate. Further more, your functions aren't really comparable. In the one case you've defined a function with a receiver of type Person
and it is passed by value, meaning a copy of the instance is made and pushed onto the stack. In the other instance the function is stand alone and you pass a Person
reference to it. You can also, do pass by reference with an 'instance method' by making the receiving type a pointer.
So in the first case;
func (p Person) fullname() string {
return p.firstname + p.lastname
}
you call the function like p.fullname()
and a copy of p
is pushed onto the stack. If you did assignment in this function, p
would not be modified, the instance in the scope would be modified. So for setters, or any function that is intended to change the objects state, this isn't an option at all.
To elaborate on the alternative I was talking about, you could instead do this;
func (p *Person) fullname() string {
return p.firstname + p.lastname
}
Which still allow you to call it on an instance like p.fullname()
however, what's passed to the function is a reference of type *Person
. So this would be the typical choice for implementing a function that sets a value on a struct.
Now your second example;
func fullname(p *Person) string {
return p.firstname + p.lastname
}
has no receiver and passes a pointer. Meaning you call it like this fullname(p)
. The function is not exported so it's only available in the package where it was declared, in this case main. To draw parallels to (potentially) more familiar languages, this is like defining a function in C++ where the other two are defining a 'method' or a function in a class. And the other two would be compared to 'passing by value' or 'passing by reference'. This passes by reference by is no associated to any type.
In your example I would always use func (p *Person) fullname() string
for performance reasons. In my opinion the most appropriate time to use the pass by value alternative func (p Person) fullname() string
is when you want to enforce immutability. If you want to do operations where it's better to produce a new object rather than modifying an existing one (a lot of collection libraries operate like this, for example LINQ in C# always produces a new collection and an attempt to modify the collection during a query will result in an exception) then you probably want a value type receiver.
Hope that helps. I can update with more information if any of this is still the cause of confusion.