Skip to content
gohacks.dev
Basics

The fmt Package in Go and Its Full List of Print Verbs

Every fmt verb worth knowing in one table with real output: %v, %+v and %#v, numbers, strings, width and flags, and the error markers.

6 min read
Summarize in AI
Flat illustration of an editor window full of code.

fmt is the first package every Go program imports and the one whose documentation people rarely read to the end. Most of the time %v and %d are enough. This page is the whole list, grouped by what you are printing, each with the output Go 1.25 produced for it, so you can find a verb by what it looks like. The program that produced the output is at the bottom, and the rows that go wrong on purpose are collected in a table at the end. It is a companion to formatting time and print a struct in a debug friendly way, and part of the Go basics path.

Tested with Go 1.25.6, macOS on Apple Silicon (arm64)

General verbs: any value

VerbPrintsExample output for User{"ada", 36, ["x"]}
%vThe default format{ada 36 [x]}
%+vStruct with field names{Name:ada Age:36 tags:[x]}
%#vGo syntax, with typemain.User{Name:"ada", Age:36, tags:[]string{"x"}}
%TThe typemain.User
%%A literal percent sign%

A pointer to a struct prints as &{ada 36 [x]} with %v, and with field names for %+v. Maps print in sorted key order, map[a:1 b:2], regardless of insertion order, which makes output stable. Slices print as [1 2], a nil pointer as <nil>, and a nil interface as <nil> too.

Integers, characters and booleans

VerbPrintsOutput
%dDecimal255
%bBinary101 for 5
%oOctal10 for 8
%OOctal with 0o prefix0o10 for 8
%x, %XHex, lower or upperff, FF
%cThe character for a code pointG
%qA quoted character'G'
%UUnicode notationU+0047
%tA booleantrue

Floating point

VerbPrintsOutput for the shown value
%fDecimal, six digits by default3.141590 for 3.14159
%.2fTwo digits after the point3.14
%8.3fWidth 8, three digits 3.142
%eScientific1.234568e+05 for 123456.789
%gThe shortest of %e and %f that is exact123456.789, and 1e+21 for 1e21

%g is what %v uses for floats, so fmt.Println(0.1) prints 0.1. For money display, use %.2f, and remember that formatting rounds the output and not the value, a point made in the floating point article.

Strings and bytes

VerbPrintsOutput
%sThe string, or a []byte as textgo, hi
%qDouble quoted and escaped"go"
%xHex of each byte676f for "go"
% xHex with spaces between bytes67 6f
%10sRight aligned in 10 columns go
%-10sLeft aligned in 10 columnsgo

For a []byte, %v prints the numbers, [104 105], and %s prints the text, which is the most common surprise when printing bytes. For a []string, %q quotes each element, which makes empty strings and spaces visible, and I use it whenever I debug a list of strings.

Width, precision and flags

Between the % and the verb, three optional parts control layout: flags, width and precision. A * takes the width or precision from an argument.

WriteMeaningOutput
%5dWidth 5, right aligned[ 42]
%-5dLeft aligned[42 ]
%05dZero padded[00042]
%+dAlways show the sign[+42]
%#xAdd the 0x prefix0xbee
%#oAdd the 0 prefix010
%*dWidth taken from an argument 42 for width 6
%[2]s %[1]sPick arguments by positionb a
%6.2f%%Width, precision and a literal percent 12.35%

The explicit argument index %[1]s is useful when the same value appears twice in a format, as in "%[1]s: %[1]q", and when translating messages whose word order differs.

Types with methods

If a value has a String() string method, %v and %s call it, and %d still prints the underlying number. Errors with an Error() string method print through it for %v and %s. Use %w instead of %v inside fmt.Errorf to wrap an error so errors.Is and errors.As can find it. The catch with pointer receivers is covered in the pointer receiver error: a String on *T is not called when you print a T.

What the output looked like

All of the above came from one program. The wrong ones are on purpose, to show the markers:

main.goGo
package main

import (
	"errors"
	"fmt"
)

type User struct {
	Name string
	Age  int
	tags []string
}

type Level int

func (l Level) String() string { return [...]string{"low", "high"}[l] }

func main() {
	u := User{"ada", 36, []string{"x"}}
	p := &u
	fmt.Printf("%v | %+v | %#v | %T\n", u, u, u, u)
	fmt.Printf("%v | %+v\n", p, p)
	fmt.Printf("%d %b %o %x %X %c %q %U\n", 255, 5, 8, 255, 255, 'G', 'G', 'G')
	fmt.Printf("%f %.2f %8.3f %e %g %g\n", 3.14159, 3.14159, 3.14159, 123456.789, 123456.789, 1e21)
	fmt.Printf("%s %q %x % x %10s|%-10s|\n", "go", "go", "go", "go", "go", "go")
	fmt.Printf("%t %v %5t|\n", true, false, true)
	fmt.Printf("[%5d] [%-5d] [%05d] [%+d] [%x] [%#x] [%#o]\n", 42, 42, 42, 42, 3054, 3054, 8)
	fmt.Printf("%v %d %s %q\n", Level(1), Level(1), Level(1), Level(1))
	fmt.Printf("%[2]s %[1]s %[2]s\n", "a", "b")
	fmt.Printf("%*d|%-*d|\n", 6, 42, 6, 42)
	fmt.Printf("%d %s\n", "text", 5)
	fmt.Printf("%d\n")
	fmt.Printf("%d\n", 1, 2)
	fmt.Printf("%v %v %v\n", []int{1, 2}, map[string]int{"b": 2, "a": 1}, [2]bool{true})
	fmt.Printf("%q %v\n", []string{"a b", "c"}, []byte("hi"))
	fmt.Printf("%s %x\n", []byte("hi"), []byte("hi"))
	err := fmt.Errorf("load: %w", errors.New("disk full"))
	fmt.Printf("%v | %q\n", err, err)
	fmt.Printf("100%%\n")
	fmt.Println("a", 1, 2, "b", "c", 3.0, nil)
	fmt.Print("a", 1, 2, "b", "c", 3.0, "\n")
	fmt.Printf("%6.2f%%\n", 12.3456)
}
Text
{ada 36 [x]} | {Name:ada Age:36 tags:[x]} | main.User{Name:"ada", Age:36, tags:[]string{"x"}} | main.User
&{ada 36 [x]} | &{Name:ada Age:36 tags:[x]}
255 101 10 ff FF G 'G' U+0047
3.141590 3.14    3.142 1.234568e+05 123456.789 1e+21
go "go" 676f 67 6f         go|go        |
true false  true|
[   42] [42   ] [00042] [+42] [bee] [0xbee] [010]
high 1 high "high"
b a b
    42|42    |
%!d(string=text) %!s(int=5)
%!d(MISSING)
1
%!(EXTRA int=2)[1 2] map[a:1 b:2] [true false]
["a b" "c"] [104 105]
hi 6869
load: disk full | "load: disk full"
100%
a 1 2 b c 3 <nil>
a1 2bc3
 12.35%

Note the three markers that begin with %! in the output. A verb that does not fit its argument prints %!d(string=text), with the verb, the actual type and the value, instead of panicking, since a logging call that crashes the program is worse than one that prints something odd. A format with no matching argument prints %!d(MISSING). An argument with no verb, as in the second %d line, prints %!(EXTRA int=2) after the format's own newline, so the marker lands at the start of whatever prints next, in front of the slice line here. None of these stops the program, which is why they survive in production. go vet knows the format verbs and reports all three before you ship:

Shell
go vet ./...

Println writes its operands separated by spaces and ends with a newline, whatever the types. Print adds a space between operands only when neither is a string, so a call mixing strings and numbers, as in the last two print lines above, runs the strings together while the numbers keep their spaces: a1 2bc3. That is an odd rule, and the reason to use Println or Printf unless you have a specific need. Sprintf returns a string, Fprintf writes to an io.Writer such as a file or a strings.Builder, and Errorf builds an error. The S, F and Errorf variants all take the same verbs.

What is not on this list

The scanning side, fmt.Sscanf and fmt.Fscan, uses a related but smaller set of verbs and is rarely the right tool for parsing; reading user input and strconv are better. Custom formatting for a type is possible by implementing Format(f fmt.State, verb rune), which almost no code needs, and GoStringer changes what %#v prints. For structured logs, use log/slog and pass key and value pairs instead of building a message string. The complete rules, including every flag, are in the fmt package documentation, and the go vet printf check explains what it can and cannot detect.

Share

Keep reading

Get new articles by email

We send one email when a new article is published. No spam.

Unsubscribe in one click. No spam, ever.