Why isn't my Stringer interface method getting invoked? When using fmt.Println

When calling fmt.Println, myCar is implicitly converted to a value of type interface{} as you can see from the function signature. The code from the fmt package then does a type switch to figure out how to print this value, looking something like this:

switch v := v.(type) {
case string:
    os.Stdout.WriteString(v)
case fmt.Stringer:
    os.Stdout.WriteString(v.String())
// ...
}

However, the fmt.Stringer case fails because Car doesn't implement String (as it is defined on *Car). Calling String manually works because the compiler sees that String needs a *Car and thus automatically converts myCar.String() to (&myCar).String(). For anything regarding interfaces, you have to do it manually. So you either have to implement String on Car or always pass a pointer to fmt.Println:

fmt.Println(&myCar)

Methods

Pointers vs. Values

The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers. This is because pointer methods can modify the receiver; invoking them on a copy of the value would cause those modifications to be discarded.

Therefore, for your String method to work when invoked on both pointers and values, use a value receiver. For example,

package main

import "fmt"

type Car struct {
    year int
    make string
}

func (c Car) String() string {
    return fmt.Sprintf("{make:%s, year:%d}", c.make, c.year)
}

func main() {
    myCar := Car{year: 1996, make: "Toyota"}
    fmt.Println(myCar)
    fmt.Println(&myCar)
}

Output:

{make:Toyota, year:1996}
{make:Toyota, year:1996}

Define your fmt.Stringer on a pointer receiver:

package main

import "fmt"

type Car struct {
        year int
        make string
}

func (c *Car) String() string {
        return fmt.Sprintf("{maker:%s, produced:%d}", c.make, c.year)
}

func main() {
        myCar := Car{year: 1996, make: "Toyota"}
        myOtherCar := &Car{year: 2013, make: "Honda"}
        fmt.Println(&myCar)
        fmt.Println(myOtherCar)
}

Playground


Output:

{maker:Toyota, produced:1996}
{maker:Honda, produced:2013}    

Then, always pass a pointer to instances of Car to fmt.Println. This way a potentially expensive value copy is avoided under your control.

Tags:

Go