Golang fmt.State Interface
last modified May 8, 2025
This tutorial explains how to use the fmt.State
interface in Go.
We'll cover interface methods with practical examples of custom formatting.
The fmt.State interface is used for custom formatting in Go. It provides access to formatting flags and options. Implementers can inspect formatting directives and write formatted output.
In Go, fmt.State
is typically used with the fmt.Formatter
interface. It allows types to control how they're printed with format verbs
like %v
, %s
, or custom verbs.
Basic fmt.State interface definition
The fmt.State
interface has three methods. This example shows the
interface definition and basic usage.
package main import "fmt" // fmt.State interface definition: /* type State interface { Write(b []byte) (n int, err error) Width() (wid int, ok bool) Precision() (prec int, ok bool) Flag(c int) bool } */ func main() { // The fmt package passes a fmt.State implementation // to Formatter.Format method when formatting values fmt.Println("See examples below for practical usage") }
The interface provides width, precision and flag information. The Write
method outputs formatted text. These are used together in custom formatters.
Implementing a custom formatter
We can create types that implement fmt.Formatter
using fmt.State
.
This example shows a basic custom formatter implementation.
package main import ( "fmt" "strings" ) type MyType struct { value string } func (m MyType) Format(f fmt.State, verb rune) { switch verb { case 'v': if f.Flag('+') { fmt.Fprintf(f, "MyType{value: %q}", m.value) } else { fmt.Fprintf(f, "%s", m.value) } case 's': fmt.Fprintf(f, "%s", strings.ToUpper(m.value)) default: fmt.Fprintf(f, "%%!%c(MyType=%s)", verb, m.value) } } func main() { val := MyType{"hello"} fmt.Printf("%v\n", val) // hello fmt.Printf("%+v\n", val) // MyType{value: "hello"} fmt.Printf("%s\n", val) // HELLO fmt.Printf("%x\n", val) // %!x(MyType=hello) }
The Format
method handles different format verbs. It uses fmt.State
to check flags and write output. This provides complete control over formatting.
Using Width and Precision
The Width
and Precision
methods provide formatting details.
This example demonstrates their usage in custom formatting.
package main import "fmt" type BoxedString string func (b BoxedString) Format(f fmt.State, verb rune) { width, hasWidth := f.Width() prec, hasPrec := f.Precision() if !hasWidth { width = len(b) + 4 } if !hasPrec { prec = len(b) } if prec > len(b) { prec = len(b) } topBottom := strings.Repeat("*", width) content := fmt.Sprintf("* %.*s %*s *", prec, b, width-prec-5, "") fmt.Fprintln(f, topBottom) fmt.Fprintln(f, content) fmt.Fprint(f, topBottom) } func main() { msg := BoxedString("Hello, World!") fmt.Printf("%v\n", msg) fmt.Printf("%10.5v\n", msg) }
The formatter uses width and precision to control output. It creates a box around the string with dynamic sizing. Different format verbs produce varied output layouts.
Checking Format Flags
The Flag
method checks for formatting flags like '+', '-', or '#'.
This example shows how to handle different flags in formatting.
package main import ( "fmt" "strconv" ) type Temperature float64 func (t Temperature) Format(f fmt.State, verb rune) { switch verb { case 'v', 'f', 'g': if f.Flag('#') { fmt.Fprintf(f, "Temperature(%v)", float64(t)) return } if f.Flag('+') { fmt.Fprintf(f, "%+.2f°C", float64(t)) return } fmt.Fprintf(f, "%.2f°C", float64(t)) case 's': fmt.Fprintf(f, strconv.FormatFloat(float64(t), 'f', -1, 64)) default: fmt.Fprintf(f, "%%!%c(Temperature=%v)", verb, t) } } func main() { temp := Temperature(23.456) fmt.Printf("%v\n", temp) // 23.46°C fmt.Printf("%+#v\n", temp) // Temperature(23.456) fmt.Printf("%+v\n", temp) // +23.46°C fmt.Printf("%s\n", temp) // 23.456 }
The formatter checks for '#' and '+' flags with Flag()
. It produces
different output based on the flags present. This allows flexible formatting
options.
Custom Verb Handling
We can define custom format verbs using fmt.State
. This example
shows how to implement a custom 'b' verb for binary output.
package main import ( "fmt" "strconv" ) type BinaryInt int func (b BinaryInt) Format(f fmt.State, verb rune) { switch verb { case 'b': width, _ := f.Width() s := strconv.FormatInt(int64(b), 2) if width > len(s) { s = fmt.Sprintf("%0*s", width, s) } fmt.Fprint(f, s) case 'v', 'd': fmt.Fprintf(f, "%d", int(b)) default: fmt.Fprintf(f, "%%!%c(BinaryInt=%d)", verb, b) } } func main() { num := BinaryInt(42) fmt.Printf("%b\n", num) // 101010 fmt.Printf("%8b\n", num) // 00101010 fmt.Printf("%v\n", num) // 42 fmt.Printf("%x\n", num) // %!x(BinaryInt=42) }
The custom 'b' verb outputs the integer in binary format. It respects width specifiers for padding. Other verbs fall back to default formatting or error messages.
Combining with Stringer interface
fmt.State
can be combined with fmt.Stringer
. This
example shows how to provide different string representations.
package main import ( "fmt" "strings" ) type Secret string func (s Secret) String() string { return "*****" // Default string representation } func (s Secret) Format(f fmt.State, verb rune) { switch verb { case 'v': if f.Flag('#') { fmt.Fprintf(f, "Secret(%q)", string(s)) } else if f.Flag('+') { fmt.Fprintf(f, "%s (length: %d)", s.String(), len(s)) } else { fmt.Fprintf(f, "%s", s.String()) } case 's': fmt.Fprintf(f, "%s", s.String()) case 'q': fmt.Fprintf(f, "%q", s.String()) case 'x': fmt.Fprintf(f, "%x", []byte(s)) default: fmt.Fprintf(f, "%%!%c(Secret=%s)", verb, s.String()) } } func main() { sec := Secret("password123") fmt.Println(sec) // ***** fmt.Printf("%#v\n", sec) // Secret("password123") fmt.Printf("%+v\n", sec) // ***** (length: 11) fmt.Printf("%x\n", sec) // 70617373776f7264313233 }
The type provides a simple String()
method for basic output. The
Format
method offers detailed formatting options. This combination
provides flexibility in output representation.
Advanced State Usage
For complex formatting, we can fully utilize fmt.State
methods.
This example shows advanced width, precision and flag handling.
package main import ( "fmt" "strings" ) type ProgressBar float64 // 0.0 to 1.0 func (p ProgressBar) Format(f fmt.State, verb rune) { width := 20 if w, ok := f.Width(); ok { width = w } fill := int(float64(width) * float64(p)) if fill > width { fill = width } bar := strings.Repeat("=", fill) + strings.Repeat(" ", width-fill) percent := int(100 * float64(p)) if f.Flag('+') { fmt.Fprintf(f, "[%s] %3d%%", bar, percent) } else if f.Flag('-') { fmt.Fprintf(f, "%3d%% [%s]", percent, bar) } else { fmt.Fprintf(f, "[%s]", bar) } } func main() { p := ProgressBar(0.65) fmt.Printf("%v\n", p) // [============ ] fmt.Printf("%+v\n", p) // [============ ] 65% fmt.Printf("%-v\n", p) // 65% [============ ] fmt.Printf("%30v\n", p) // [========================== ] fmt.Printf("%+30v\n", p) // [========================== ] 65% }
The progress bar formatter dynamically adjusts based on width. It uses flags to
control percentage display position. This demonstrates comprehensive fmt.State
usage for rich formatting.
Source
This tutorial covered the fmt.State
interface in Go with practical
examples of custom formatting implementations.
Author
List all Golang tutorials.