← Files GophersARCHIVED FILE
skills/go-functions/references/printf-and-stringer.md
2.89 KB · Oct 5, 2026 · 18:31 UTC
# Printf, Stringer, and Format
## Common Verbs
| Verb | Use |
|---|---|
| `%v` | Default value formatting |
| `%+v` | Struct with field names |
| `%#v` | Go syntax representation |
| `%T` | Type of the value |
| `%s` | String / byte slice / `Stringer` |
| `%q` | Double-quoted, escaped string |
| `%d` | Decimal integer |
| `%x` `%X` | Hex (lower / upper) |
| `%w` | Wrap error (only in `fmt.Errorf`) |
When formatting user input or arbitrary keys into an error, use `%q`. It quotes and escapes — your error stays readable even when the input contains tabs, newlines, or quotes.
```go
return fmt.Errorf("unknown key %q", key)
// → unknown key "weird\nname"
```
## Naming Format Functions
Functions accepting a `format string` end in `f`:
```go
func Logf(format string, args ...any)
func Errorf(format string, args ...any) error
```
`go vet` checks the format/argument match — but only when the function name ends in `f`.
Use a `const` for format strings reused outside `Printf` calls; `vet` validates the constant.
## Stringer
```go
type Severity int
const (
Info Severity = iota + 1
Warn
Error
)
func (s Severity) String() string {
switch s {
case Info:
return "INFO"
case Warn:
return "WARN"
case Error:
return "ERROR"
default:
return fmt.Sprintf("Severity(%d)", int(s))
}
}
```
Notes:
- The default case must never recurse into `%s`/`%v` of `s` — that would call `String()` again.
- Generate with `//go:generate stringer -type=Severity` for enums where order is stable.
## Infinite Recursion in String()
```go
type Bad struct{ N int }
func (b Bad) String() string { return fmt.Sprintf("%v", b) } // infinite recursion
```
`%v` on `b` calls `String()`. The fix:
```go
func (b Bad) String() string { return fmt.Sprintf("Bad(%d)", b.N) }
```
Or to print the underlying type without recursion:
```go
func (b Bad) String() string {
type alias Bad
return fmt.Sprintf("%+v", alias(b))
}
```
## fmt.GoStringer
`%#v` invokes `GoString()`. Implement it when the default Go syntax representation isn't useful (sensitive data, custom debug shapes).
## fmt.Formatter
For full control, implement:
```go
func (b Big) Format(f fmt.State, verb rune) { ... }
```
This is rare. Reach for it when you need to:
- Distinguish between `%v`, `%s`, `%q`, and `%d` for the same type.
- Honor width / precision flags (`%10.2v`).
## Errors and Format
`%w` only works in `fmt.Errorf`, and it must wrap a single error:
```go
return fmt.Errorf("opening %s: %w", path, err)
```
`errors.Is` and `errors.As` then walk the wrapped chain. Use `%v` instead of `%w` when you specifically want to *hide* the underlying error type from callers.
## Don't Concatenate Strings into Errors
```go
// Bad
return errors.New("opening " + path + ": " + err.Error())
// Good
return fmt.Errorf("opening %s: %w", path, err)
```
The wrap preserves identity; the concatenation throws it away.
SHA-256: 3c345ec66aa272dd94699b21a8f537ffdfba7fe592e8d1340697126469845529