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.