Skip to main content
eLearner.app
Module 8 · Lesson 1 of 536/50 in the course~10 min
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 to os.Stdout (Print, Println, Printf).
  • Fprint* → writes to any io.Writer (Fprintln(os.Stderr, ...), Fprintf(file, ...)).
  • Sprint* → returns a string without printing (Sprintf, Sprintln).
  • Errorf → builds a formatted error.
Go
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 error

The 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

VerbWhat it does
%vDefault value (calls String() if implemented).
%+vStruct with field names.
%#vGo syntax (useful for debugging and snapshots).
%TDynamic type of the value.
%dInteger in base 10.
%b %o %x %XBinary, octal, hexadecimal (lowercase/uppercase).
%f %e %gFloating point: fixed, exponential, compact.
%sString (or error/Stringer).
%qGo-style quoted string (escapes included).
%cUnicode character (rune).
%pPointer in hexadecimal.
%wError wrap (only inside Errorf).
Go
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.Point

Width, 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 with 0 (flag 0).
  • %.2f → precision 2 decimal digits.
  • %8.2f → width 8, precision 2.
Go
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.

Go
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

Exercise#go.m8.l1.e1
Attempts: 0Loading…

Use fmt.Sprintf to format the string '[42]' from the integer 42, then print it.

Loading editor…

Solution available after 3 attempts

Exercise#go.m8.l1.e2
Attempts: 0Loading…

Print the struct p with the verb %+v (it must include the field names).

Loading editor…

Solution available after 3 attempts

Quiz#go.m8.l1.e3
Ready

Which verb, used in fmt.Errorf, preserves the error chain for errors.Is/As?

Go
err := fmt.Errorf("read %q: ???", path, base)
Answer options