ZetCode

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.

Note: Implementers must handle all three methods.
state_interface.go
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.

custom_formatter.go
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.

width_precision.go
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.

format_flags.go
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.

custom_verb.go
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.

stringer_combination.go
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.

advanced_state.go
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

Go fmt.State documentation

This tutorial covered the fmt.State interface in Go with practical examples of custom formatting implementations.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Golang tutorials.