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(Formatmethod) is used for every verb. Otherwise, for the verbs%v %s %q %x %X(andPrint/Println),fmtcheckserror, thenStringer.Error()takes precedence overString().%dand%fdo not call these methods. - The method is found only if it is in the method set of the value passed. If
Stringhas a pointer receiver and you print a value (fmt.Println(t)),Stringis not called and the struct is printed field by field. %+vstill callsString/Error.%#vdoes not: it callsGoStringif the type implementsfmt.GoStringer, and otherwise prints the Go-syntax representation (fmt.Printf("%#v", Celsius(1))prints1).
More on Interfaces, Methods & Embedding
- Q95Explain "accept interfaces, return structs". When is returning an interface justified?
- Q96Why does Go favour small interfaces? Give examples of composition.
- Q98Design custom errors in Go. Explain wrapping,
errors.Is,errors.Asand multi-error trees. - Q99When does comparing two interface values panic? What does this print?
- Q100Does converting a value to an interface allocate? How do interfaces affect escape analysis and performance?
- Q101What's the difference between a method value and a method expression? What does this print?