DMarlon Studio

Lesson 4 · 4 of 20

Stringer and formatting your types · Go

Decide how your Go types print with a String method (fmt.Stringer) and see what %v, %+v, %#v, %d and widths do with them.

Go · Stringer and formatting your types in DMarlon Studio: the example's code and its output in the terminal

fmt looks for a String() string method on every value; if it has one, the value satisfies the `fmt.Stringer` interface and is printed with it. Status is an integer with constants made with `iota` (0, 1, 2), and its String turns them into words with an array: Shipped prints as shipped, not 1.

Money stores cents in an int64 so no rounding errors creep in, and its String writes them as 259.99 EUR. That is why fmt.Println(o.Status, o.Total) gives readable text with no manual conversion.

With a struct, %v prints the fields between braces and uses each one's String: {1042 259.99 EUR shipped}. `%+v` adds the field names, very handy for debugging. `%#v` prints Go syntax (main.Order{ID:1042, Total:25999, Status:1}) and does not call String.

Only the text verbs (%v, %s, %q, %x) call String. With `%d` you get the number underneath: 1 25999. Widths apply to the text String returns: %-12s aligns left and %12v aligns right.

One trap: inside String, do not print the value itself with %v or %s, because fmt would call String again forever. Money uses %d, which does not call it, so all is well.

Code · main.go

package mainimport "fmt"type Status intconst (	Pending Status = iota	Shipped	Delivered)// String makes Status a fmt.Stringer: %v and %s call it.func (s Status) String() string { return [...]string{"pending", "shipped", "delivered"}[s] }type Money int64 // amount in centsfunc (m Money) String() string { return fmt.Sprintf("%d.%02d EUR", m/100, m%100) }type Order struct {	ID     int	Total  Money	Status Status}func main() {	o := Order{ID: 1042, Total: 25999, Status: Shipped}	fmt.Println("status:", o.Status, "| total:", o.Total)	fmt.Printf("%v\n", o)	fmt.Printf("%+v\n", o)	fmt.Printf("%#v\n", o)                           // Go syntax: String is not used	fmt.Printf("%d %d\n", o.Status, o.Total)         // %d prints the plain number	fmt.Printf("|%-12s|%12v|\n", Delivered, o.Total) // width and alignment}

Output · go run main.go

status: shipped | total: 259.99 EUR
{1042 259.99 EUR shipped}
{ID:1042 Total:259.99 EUR Status:shipped}
main.Order{ID:1042, Total:25999, Status:1}
1 25999
|delivered   |  259.99 EUR|

Key points

  • A type with String() string is a fmt.Stringer.
  • %v and %s use String; %d prints the number.
  • %+v adds field names; %#v prints Go syntax.
  • Widths (%-12s) apply to the text from String.
  • Inside String, never format the value itself with %v.

Exercise

Add a Cancelled status to the constants and to the table in String, and set the order to it. Then print fmt.Printf("%q\n", o.Status) and notice that %q uses String too.