As a programmer coming from other languages like C++, I find it rather strange that go allows to specify methods for structs that allow either a pointer or an instance as a parameter. According to go by example once could use either of them if we didn't want to modify the origin:
Go automatically handles conversion between values and pointers for method calls. You may want to use a pointer receiver type to avoid copying on method calls or to allow the method to mutate the receiving struct.
Consider the following code:
package main
import (
"fmt"
)
type Foo struct {}
type Bar struct {}
func (this Foo) String() string {
return "Foo"
}
func (this *Bar) String() string {
return "Bar"
}
func main() {
fmt.Println(Foo{}) // "Foo"
fmt.Println(Bar{}) // "{}"
}
Why can't I use both signature versions to modify the stringify (I don't know how it is actually called in go) behavior of the structs?
Just to be clear: I don't really care about the stringify, but want to understand how the language behaves.
Just add &
to the Bar{}
and make it pointer receiver, like this:
fmt.Println(&Bar{}) // "Bar"
Here a little adjustment to your code that outputs:
Foo
Bar
see:
package main
import "fmt"
type Foo struct{}
func (Foo) String() string {
return "Foo"
}
type Bar struct{}
func (*Bar) String() string {
return "Bar"
}
func main() {
fmt.Println(Foo{}) // "Foo"
pb := &Bar{}
fmt.Println(pb) // "Bar"
}
Notes:
Receiver name should be a reflection of its identity; don't use generic names such as "this" or "self"
And you don't need names here for your example.
And nice to read Golang methods receivers:
Value receivers operate on a copy of the original type value. This means that there is a cost involved, especially if the struct is very large, and pointer received are more efficient.
Because Bar does not implement stringify
*Bar does.
If you remove implementation of stringify
from Foo, you will get "{}".
Similarly, When you write fmt.Println(Bar{})
it means it will look for something like func (Bar) String()
and not func (*Bar) String()
Additioanlly , story is different when you write fmt.Println(&Foo{})
, you might think it will print "{}" because there is no func (*Foo) String()
but it will print "Foo".
For that, you will have to understand Interfaces. These are my experiences so please do your own research too. The fmt.Print
function uses String() from stringify. So actually the String() is not called on your struct but rather than an variable of type stringify.
interface type can hold a type(which implemented it) or pointer to it, if it was implemented with value receiver. That is why
Foo{}
and&Foo{}
both work.interface type can hold a type's pointer (which implemented it)only, if it was implemented with pointer receiver. Why? Because when you implement an interface with pointer receiver, it needs an address which can only be provided with a pointer. That is why only
&Bar{}
works and notBar{}