The Value of Receiving
A Go Brain Teaser
What do you think the following program will print?
package mainimport (
"fmt"
)type Coordinate struct {
Lat float64
Lng float64
}func (c *Coordinate) String() string {
return fmt.Sprintf("%f/%f", c.Lat, c.Lng)
}func main() {
c := Coordinate{32.5253837, 34.9408283}
fmt.Println(c)
}
This program will print: {32.5253837 34.9408283}
.
Wait! What? Coordinate
implements the fmt.Stringer
interface. Shouldn't you see 32.5253837/34.9408283
?
Let’s do a single character change to the program and try again:
fmt.Println(&c)
Now you’ll see 32.525384/34.940828
printed out.
When we pass a value of a Coordinate
to fmt.Println
, Go prints the default struct format. When we pass a pointer to a Coordinate
to fmt.Println
it does use our custom printing.
The Go spec is a good place to start. It says:
— The method set of a defined type T consists of all methods declared with receiver type T.
— The method set of a pointer to a defined type T (where T is neither a pointer nor an interface) is the set of all methods declared with receiver *T or T.
Our code fits the first case, Coordinate.Stringer
is defined on a pointer receiver, yet we pass a value of Coordinate
to fmt.Println
. As far as Go is concerned, we didn't pass a parameter that implements fmt.Stringer
to fmt.Println
so fmt.Println
prints the default struct formatting.
If you’re curious why Go behaves this way, you can read the FAQ. Or, even better, can watch Bill Kennedy explain why this behavior is a sign that Go loves you.
If you like to solve programming problems, check out Miki Tebeka’s Brain Teaser books from The Pragmatic Bookshelf. You can save 35 percent on the ebook versions with promo code brain_teasers_35 through August 30, 2022: