Golang fmt.Formatter Interface
last modified May 8, 2025
This tutorial explains how to use the fmt.Formatter
interface in Go.
We'll cover interface basics with practical examples of custom formatting.
The fmt.Formatter interface allows types to control their formatting. It provides more flexibility than the Stringer interface by supporting format verbs and flags.
In Go, fmt.Formatter
is used when you need custom formatting
behavior beyond simple string conversion. It works with all fmt printing
functions like Printf and Sprintf.
Basic fmt.Formatter interface definition
The fmt.Formatter
interface requires implementing one method.
This example shows the interface definition and basic implementation.
Note: The Format method gives you full control over formatting.
package main import "fmt" type Point struct { X, Y int } func (p Point) Format(f fmt.State, verb rune) { switch verb { case 'v': if f.Flag('+') { fmt.Fprintf(f, "Point{X: %d, Y: %d}", p.X, p.Y) } else { fmt.Fprintf(f, "{%d %d}", p.X, p.Y) } case 's': fmt.Fprintf(f, "(%d,%d)", p.X, p.Y) default: fmt.Fprintf(f, "%%!%c(Point=%d,%d)", verb, p.X, p.Y) } } func main() { p := Point{3, 4} fmt.Printf("%v\n", p) // {3 4} fmt.Printf("%+v\n", p) // Point{X: 3, Y: 4} fmt.Printf("%s\n", p) // (3,4) fmt.Printf("%d\n", p) // %!d(Point=3,4) }
The Point type implements Format to handle different format verbs. It supports 'v' with + flag and 's' verb with custom formatting.
Handling width and precision
The fmt.State parameter provides access to formatting flags. This example shows how to handle width and precision specifications.
package main import ( "fmt" "strings" ) type Banner string func (b Banner) Format(f fmt.State, verb rune) { switch verb { case 's', 'v': width, hasWidth := f.Width() if hasWidth { padded := strings.Repeat(string(b), width/len(b)+1) fmt.Fprint(f, padded[:width]) } else { fmt.Fprint(f, string(b)) } default: fmt.Fprintf(f, "%%!%c(Banner=%s)", verb, b) } } func main() { b := Banner("Go") fmt.Printf("%s\n", b) // Go fmt.Printf("%5s\n", b) // GoGoG fmt.Printf("%.3s\n", b) // Go fmt.Printf("%5.3s\n", b) // Go }
The Banner type uses Width() to check for width specification. It repeats the banner text to fill the requested width when printing.
Custom flag handling
The fmt.State interface provides flag information. This example demonstrates custom flag handling in a formatter.
package main import "fmt" type Temperature float64 func (t Temperature) Format(f fmt.State, verb rune) { switch verb { case 'f', 'F': prec, hasPrec := f.Precision() if !hasPrec { prec = 1 } if f.Flag('+') { fmt.Fprintf(f, "%+.*f°F", prec, float64(t)) } else { fmt.Fprintf(f, "%.*f°C", prec, float64(t)*5/9) } default: fmt.Fprintf(f, "%%!%c(Temperature=%f)", verb, t) } } func main() { temp := Temperature(32.0) fmt.Printf("%f\n", temp) // 0.0°C fmt.Printf("%+.2f\n", temp) // +32.00°F fmt.Printf("%5.1f\n", temp) // 0.0°C }
The Temperature type checks for '+' flag to switch between Fahrenheit and Celsius. It also handles precision specifications for decimal places.
Combining with Stringer interface
A type can implement both fmt.Stringer and fmt.Formatter. This example shows how they work together.
package main import "fmt" type Color struct { R, G, B uint8 } func (c Color) String() string { return fmt.Sprintf("RGB(%d,%d,%d)", c.R, c.G, c.B) } func (c Color) Format(f fmt.State, verb rune) { switch verb { case 'v': if f.Flag('#') { fmt.Fprintf(f, "Color{R:0x%02x, G:0x%02x, B:0x%02x}", c.R, c.G, c.B) } else { fmt.Fprint(f, c.String()) } case 's': fmt.Fprint(f, c.String()) case 'x', 'X': fmt.Fprintf(f, "%02x%02x%02x", c.R, c.G, c.B) default: fmt.Fprintf(f, "%%!%c(Color=%s)", verb, c.String()) } } func main() { c := Color{255, 0, 128} fmt.Println(c) // RGB(255,0,128) fmt.Printf("%v\n", c) // RGB(255,0,128) fmt.Printf("%#v\n", c) // Color{R:0xff, G:0x00, B:0x80} fmt.Printf("%x\n", c) // ff0080 }
The Color type uses String() for simple string conversion. The Format method provides additional formatting options including hex output and struct-style printing with the # flag.
Formatting complex numbers
The fmt.Formatter can be used to create custom complex number formatting. This example shows polar coordinate representation.
package main import ( "fmt" "math" ) type Polar complex128 func (p Polar) Format(f fmt.State, verb rune) { r := real(p) i := imag(p) switch verb { case 'v', 'f', 'g': mag := math.Hypot(r, i) ang := math.Atan2(i, r) * 180 / math.Pi prec, hasPrec := f.Precision() if !hasPrec { prec = 2 } fmt.Fprintf(f, "%.*f∠%.*f°", prec, mag, prec, ang) default: fmt.Fprintf(f, "%%!%c(Polar=%f+%fi)", verb, r, i) } } func main() { p := Polar(1 + 1i) fmt.Printf("%v\n", p) // 1.41∠45.00° fmt.Printf("%.3f\n", p) // 1.414∠45.000° fmt.Printf("%.1g\n", p) // 1.4∠45.0° }
The Polar type formats complex numbers in polar notation. It calculates magnitude and angle, then formats them with the specified precision.
Custom date formatting
Implementing fmt.Formatter allows flexible date formatting. This example shows custom date output with different verbs.
package main import ( "fmt" "time" ) type MyDate time.Time func (d MyDate) Format(f fmt.State, verb rune) { t := time.Time(d) switch verb { case 'd': fmt.Fprintf(f, "%02d/%02d/%04d", t.Day(), t.Month(), t.Year()) case 't': fmt.Fprintf(f, "%02d:%02d:%02d", t.Hour(), t.Minute(), t.Second()) case 'v': if f.Flag('+') { fmt.Fprintf(f, time.Time(d).String()) } else { fmt.Fprintf(f, "%s", t.Format("2006-01-02")) } default: fmt.Fprintf(f, "%%!%c(MyDate=%s)", verb, t.Format(time.RFC3339)) } } func main() { d := MyDate(time.Date(2023, 5, 15, 14, 30, 0, 0, time.UTC)) fmt.Printf("%d\n", d) // 15/05/2023 fmt.Printf("%t\n", d) // 14:30:00 fmt.Printf("%v\n", d) // 2023-05-15 fmt.Printf("%+v\n", d) // 2023-05-15 14:30:00 +0000 UTC }
The MyDate type provides custom formatting for dates. It supports 'd' for date, 't' for time, and 'v' with + flag for detailed output.
Binary data formatting
The fmt.Formatter can format binary data in different representations. This example shows hex dump and binary output.
package main import ( "fmt" "strings" ) type Binary []byte func (b Binary) Format(f fmt.State, verb rune) { switch verb { case 'x', 'X': for _, byteVal := range b { fmt.Fprintf(f, "%02x", byteVal) } case 'b': for _, byteVal := range b { fmt.Fprintf(f, "%08b ", byteVal) } case 'v': if f.Flag('#') { fmt.Fprintf(f, "Binary{0x%x}", b) } else { fmt.Fprintf(f, "%x", b) } default: fmt.Fprintf(f, "%%!%c(Binary=%x)", verb, b) } } func main() { data := Binary{0xDE, 0xAD, 0xBE, 0xEF} fmt.Printf("%x\n", data) // deadbeef fmt.Printf("%X\n", data) // DEADBEEF fmt.Printf("%b\n", data) // 11011110 10101101 10111110 11101111 fmt.Printf("%#v\n", data) // Binary{0xdeadbeef} }
The Binary type formats byte slices as hex or binary. The '#' flag triggers Go-syntax representation when used with 'v' verb.
Source
This tutorial covered the fmt.Formatter
interface in Go with
practical examples of custom formatting for different types.
Author
List all Golang tutorials.