Formatting with fmt¶
fmt.Println has carried every example so far. The rest of the package
is a small formatting language, and two of its verbs — %+v for
debugging and %w for wrapping errors — come up constantly.
type Point struct {
X, Y int
name string
}
p := Point{X: 1, Y: 2, name: "origin"}
fmt.Printf("%v\n", p) // output: {1 2 origin}
fmt.Printf("%+v\n", p) // output: {X:1 Y:2 name:origin}
The four functions¶
Every formatting function is one of these, with a prefix saying where the result goes:
| Prefix | Destination | Example |
|---|---|---|
| (none) | standard output | fmt.Printf("%d\n", n) |
S |
returned as a string |
s := fmt.Sprintf("%d", n) |
F |
an io.Writer you pass |
fmt.Fprintf(w, "%d", n) |
Errorf |
returned as an error |
fmt.Errorf("bad %d", n) |
Print writes its arguments, Println adds spaces and a newline,
Printf takes a format string. Fprintf is the one that makes the
package composable — it writes to anything implementing io.Writer,
which includes os.Stdout, a file, an HTTP response, and
strings.Builder.
%v, %+v, %#v and %T¶
%v is the default rendering of any value. The other three are for when
you are trying to work out what you actually have:
fmt.Printf("%v\n", p) // output: {1 2 origin}
fmt.Printf("%+v\n", p) // output: {X:1 Y:2 name:origin}
fmt.Printf("%#v\n", p) // output: main.Point{X:1, Y:2, name:"origin"}
fmt.Printf("%T\n", p) // output: main.Point
%+v adds field names and is the one to reach for in a log line or a
quick debug print. %#v prints Go syntax you could paste back into
source. %T prints the type, which is how you find out what is really
inside an any.
Composite values nest sensibly, and map keys are printed in sorted order so output is reproducible:
fmt.Printf("%v\n", []int{1, 2}) // output: [1 2]
fmt.Printf("%v\n", map[string]int{"b": 2, "a": 1}) // output: map[a:1 b:2]
fmt.Printf("%v\n", &p) // output: &{1 2 origin}
A pointer to a struct prints as &{...}. A pointer to anything else
prints as a hexadecimal address, which differs every run.
Strings, quoting and runes¶
%q adds quotes and escapes — invaluable when the question is "does this
string have trailing whitespace or a tab in it?", which %s cannot
answer. It is why the strings article prints with %q.
A rune is an integer, so %v shows the number. %q shows the
character literal, %c the character, %U the code point:
fmt.Printf("%v %q\n", 'A', 'A') // output: 65 'A'
fmt.Printf("%c %U\n", 0x4e16, 0x4e16) // output: 世 U+4E16
Numbers¶
fmt.Printf("%d %b %o %x %X\n", 255, 255, 255, 255, 255)
// output: 255 11111111 377 ff FF
fmt.Printf("%f %.2f %e %g\n", 3.14159, 3.14159, 3.14159, 3.14159)
// output: 3.141590 3.14 3.141590e+00 3.14159
fmt.Printf("%t\n", true) // output: true
%f always prints six decimal places. %g picks the shortest
representation that round-trips, which is usually what you want for a
number whose magnitude you do not know in advance.
Width and precision¶
A number between % and the verb sets a minimum width; a .n sets
precision. A minus sign left-aligns, a leading zero pads with zeros:
fmt.Printf("[%5d][%-5d][%05d]\n", 42, 42, 42)
// output: [ 42][42 ][00042]
fmt.Printf("[%8.3f][%-8.3f]\n", 3.14159, 3.14159)
// output: [ 3.142][3.142 ]
On a string, precision truncates:
A * takes the width from the argument list, for when it is computed:
fmt.Stringer¶
A type with a String() string method controls its own %v and
%s rendering, and Println picks it up too:
type Temp float64
func (t Temp) String() string { return fmt.Sprintf("%.1f°C", float64(t)) }
fmt.Printf("%v\n", Temp(21.456)) // output: 21.5°C
fmt.Println(Temp(21.456)) // output: 21.5°C
The trap in a String method is formatting the receiver with a
string-like verb. fmt only consults String for %v, %s and their
relatives, so this recurses until the stack is gone:
func (t Temp) String() string { return fmt.Sprintf("%v°C", t) }
// go vet: fmt.Sprintf format %v with arg t causes recursive
// (main.Temp).String method call
Numeric verbs are safe — %.1f reads the number and never calls
String — so the float64(t) conversion above is for clarity rather
than necessity. When in doubt, convert to the underlying type: it is
correct under every verb, and go vet flags the case where it matters.
%w belongs to Errorf alone¶
%w does not format anything — it records the error for
errors.Is and errors.As, as the errors article
covers. Visually %w and %v are identical:
err := errors.New("disk full")
wrapped := fmt.Errorf("saving: %w", err)
fmt.Printf("%v | %s\n", wrapped, wrapped)
// output: saving: disk full | saving: disk full
fmt.Println(errors.Is(wrapped, err)) // output: true
Only Errorf understands %w. Passing it to Printf produces the
badness below.
When a verb and an argument disagree¶
fmt never panics on a mismatch. It writes the problem into the output,
which means a broken format string shows up in your logs rather than
taking down the process:
fmt.Printf("%d %s\n", 1) // output: 1 %!s(MISSING)
fmt.Printf("%d\n", "nope") // output: %!d(string=nope)
go vet catches both at build time, and it runs as part of go test.
From Python:
%visstr()and%#vis close torepr().Sprintfis%-formatting orf"...", but the verbs are typed — a Python f-string does not care what you hand it, whereas%dwith a string is an errorgo vetwill point at.
Quick reference¶
| Verb | Use |
|---|---|
%v |
default form of any value |
%+v |
struct with field names — the debugging default |
%#v |
Go syntax |
%T |
the type |
%s / %q |
string / quoted and escaped string |
%d %b %o %x |
integer in base 10, 2, 8, 16 |
%f %.2f %e %g |
float: fixed, 2 places, scientific, shortest |
%c / %U |
rune as a character / as U+XXXX |
%t |
bool |
%w |
wrap an error — fmt.Errorf only |
%5d %-5d %05d |
width, left-aligned, zero-padded |
%.3s |
truncate a string |