String Formatting
fmt verbs - %v, %+v, %#v, %T, width and precision, and choosing between Printf, Sprintf, and Fprintf.
The fmt package provides print functions driven by format verbs. Go's verbs are distinct from C's printf - %v prints any value in a default format, %+v adds struct field names, and %#v prints a Go syntax representation useful for debugging.
The %v verb prints values in a human-readable default format. Add + for struct field names or # for Go syntax output.
package main
import "fmt"
type Point struct {
X, Y int
}
func main() {
p := Point{3, 4}
fmt.Printf("%v\n", p) // {3 4}
fmt.Printf("%+v\n", p) // {X:3 Y:4}
fmt.Printf("%#v\n", p) // main.Point{X:3, Y:4}
fmt.Printf("%T\n", p) // main.Point
}Format integers with %d (decimal), %b (binary), %o (octal), %x (hex). Control width and zero-padding with width specifiers.
package main
import "fmt"
func main() {
n := 42
fmt.Printf("%d\n", n) // 42
fmt.Printf("%b\n", n) // 101010
fmt.Printf("%o\n", n) // 52
fmt.Printf("%x\n", n) // 2a
fmt.Printf("%X\n", n) // 2A
// Width and padding
fmt.Printf("%6d\n", n) // 42 (right-aligned)
fmt.Printf("%-6d|\n", n) // 42 (left-aligned)
fmt.Printf("%06d\n", n) // 000042 (zero-padded)
}Format floats with %f (decimal), %e (scientific), %g (compact). Control precision with %.Nf.
package main
import "fmt"
func main() {
f := 3.14159
fmt.Printf("%f\n", f) // 3.141590
fmt.Printf("%.2f\n", f) // 3.14
fmt.Printf("%8.2f\n", f) // 3.14
fmt.Printf("%e\n", f) // 3.141590e+00
fmt.Printf("%g\n", f) // 3.14159
}Use Sprintf to produce a formatted string, Fprintf to write directly to an io.Writer, and Errorf to create formatted errors (with %w for wrapping).
package main
import (
"fmt"
"os"
)
func main() {
// Sprintf returns a string
msg := fmt.Sprintf("Hello, %s! You are %d years old.", "Alice", 30)
fmt.Println(msg)
// Fprintf writes to any io.Writer (here, os.Stderr)
fmt.Fprintf(os.Stderr, "error: %s\n", "something went wrong")
// Errorf creates an error; %w wraps the error for errors.Is / errors.As
cause := fmt.Errorf("connection refused")
err := fmt.Errorf("dial database: %w", cause)
fmt.Println(err) // dial database: connection refused
}In production
%+v and %#v are invaluable in debug logs and test failure messages - they surface field names and Go syntax without writing custom String() methods. Prefer fmt.Fprintf(w, ...) over fmt.Sprintf(...) + w.Write(...) to avoid an intermediate string allocation when writing to an io.Writer. Use %w in fmt.Errorf for wrappable errors; %s or %v create a new non-wrappable error that callers cannot inspect with errors.Is or errors.As.
Enjoyed this? Get more essays on software craft delivered to your inbox.
Subscribe free