Go

What's wrong with this String() method? When does fmt call String() or Error()?

Question 97HardGo 1.22 to 1.25
type Celsius float64

func (c Celsius) String() string {
    return fmt.Sprintf("%v C", c) // infinite recursion -> stack overflow
}

%v on a value that implements fmt.Stringer calls String(), and that call is already running inside String(). The recursion never ends and the program crashes with goroutine stack exceeds 1000000000-byte limit. go vet reports: Sprintf format %v with arg c causes recursive String method call.

Fix: convert to the underlying type, which has no methods, or use a verb that does not call the method.

func (c Celsius) String() string {
    return fmt.Sprintf("%.1f C", float64(c))
}

type Temp struct{ c Celsius }
func (t Temp) String() string { return "temp=" + t.c.String() }

Rules to know:

  • A Formatter (Format method) is used for every verb. Otherwise, for the verbs %v %s %q %x %X (and Print/Println), fmt checks error, then Stringer. Error() takes precedence over String(). %d and %f do not call these methods.
  • The method is found only if it is in the method set of the value passed. If String has a pointer receiver and you print a value (fmt.Println(t)), String is not called and the struct is printed field by field.
  • %+v still calls String/Error. %#v does not: it calls GoString if the type implements fmt.GoStringer, and otherwise prints the Go-syntax representation (fmt.Printf("%#v", Celsius(1)) prints 1).

More on Interfaces, Methods & Embedding

All 35 Interfaces, Methods & Embedding questions