Module lessons (1/5)
Advanced `fmt`
The fmt package is the gateway to all textual I/O in Go. Knowing it well means writing readable logs, informative errors, and formatting values idiomatically without resorting to external libraries.
The four families
fmt exposes functions in four families, distinguished by prefix:
Print*→ writes toos.Stdout(Print,Println,Printf).Fprint*→ writes to anyio.Writer(Fprintln(os.Stderr, ...),Fprintf(file, ...)).Sprint*→ returns a string without printing (Sprintf,Sprintln).Errorf→ builds a formattederror.
fmt.Println("hello") // stdout
fmt.Fprintln(os.Stderr, "boom") // stderr
s := fmt.Sprintf("[%d]", 42) // "[42]"
err := fmt.Errorf("parse %q: %w", in, base) // wrapped errorThe difference between Print and Println is only the space between arguments and the trailing newline: Println always adds \n, Print does not, Printf follows the format.
Main verbs
| Verb | What it does |
|---|---|
%v | Default value (calls String() if implemented). |
%+v | Struct with field names. |
%#v | Go syntax (useful for debugging and snapshots). |
%T | Dynamic type of the value. |
%d | Integer in base 10. |
%b %o %x %X | Binary, octal, hexadecimal (lowercase/uppercase). |
%f %e %g | Floating point: fixed, exponential, compact. |
%s | String (or error/Stringer). |
%q | Go-style quoted string (escapes included). |
%c | Unicode character (rune). |
%p | Pointer in hexadecimal. |
%w | Error wrap (only inside Errorf). |
type Point struct{ X, Y int }
p := Point{1, 2}
fmt.Printf("%v\n", p) // {1 2}
fmt.Printf("%+v\n", p) // {X:1 Y:2}
fmt.Printf("%#v\n", p) // main.Point{X:1, Y:2}
fmt.Printf("%T\n", p) // main.PointWidth, precision, flags
Numeric and string verbs accept width and precision:
%6d→ minimum width 6, left padding with spaces.%-6d→ width 6, right padding (flag-).%06d→ width 6, padding with0(flag0).%.2f→ precision 2 decimal digits.%8.2f→ width 8, precision 2.
fmt.Printf("|%6d|\n", 42) // | 42|
fmt.Printf("|%-6d|\n", 42) // |42 |
fmt.Printf("|%06d|\n", 42) // |000042|
fmt.Printf("|%8.2f|\n", 3.14)// | 3.14|For aligned tables, fixed width + %-...s for text columns is the simplest pattern before moving to text/tabwriter.
%w and error wrapping
%w is special: it works only inside fmt.Errorf and preserves the original error in the chain, so that errors.Is and errors.As can walk it.
if _, err := os.Open(path); err != nil {
return fmt.Errorf("read config %q: %w", path, err)
}Using %v instead of %w prints the error message but breaks the chain: no unwrap, no errors.Is. Rule: if you want the caller to be able to distinguish the type or identity of the error, use %w (only one per Errorf).
Exercises
Use fmt.Sprintf to format the string '[42]' from the integer 42, then print it.
Solution available after 3 attempts
Print the struct p with the verb %+v (it must include the field names).
Solution available after 3 attempts
Which verb, used in fmt.Errorf, preserves the error chain for errors.Is/As?
err := fmt.Errorf("read %q: ???", path, base)