Golang fmt.Stringer Interface
last modified May 8, 2025
This tutorial explains how to use the fmt.Stringer interface in Go.
We'll cover interface basics with practical examples of string representation.
The fmt.Stringer interface is used to customize how types are printed.
It defines a single method String() string that returns a string
representation of the value.
In Go, implementing Stringer allows types to control their output
format when printed with functions like fmt.Println. This is useful
for debugging and logging.
Basic Stringer implementation
The simplest implementation of Stringer provides a custom string
representation for a type. This example demonstrates basic usage.
Note: The String() method must return a string value.
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years)", p.Name, p.Age)
}
func main() {
p := Person{"Alice", 25}
fmt.Println(p) // Uses our String() method
}
The Person type implements Stringer by defining a String() method. When printed, our custom format is used instead of the default struct representation.
Stringer with pointer receivers
Stringer can be implemented with pointer receivers for mutable types. This example shows pointer receiver implementation.
package main
import "fmt"
type Counter struct {
value int
}
func (c *Counter) Increment() {
c.value++
}
func (c *Counter) String() string {
return fmt.Sprintf("Counter: %d", c.value)
}
func main() {
c := &Counter{}
c.Increment()
fmt.Println(c) // Prints "Counter: 1"
}
The Counter type uses a pointer receiver for String(). This allows the method to access and format the current value after mutations.
Stringer with complex types
Stringer can format complex types with nested structures. This example shows custom formatting for a composite type.
package main
import (
"fmt"
"strings"
)
type Address struct {
Street string
City string
Country string
}
func (a Address) String() string {
return fmt.Sprintf("%s, %s, %s", a.Street, a.City, a.Country)
}
type Contact struct {
Name string
Email string
Address Address
}
func (c Contact) String() string {
return fmt.Sprintf("%s <%s>\n%s", c.Name, c.Email, c.Address)
}
func main() {
addr := Address{"123 Main St", "Springfield", "USA"}
contact := Contact{"Bob", "bob@example.com", addr}
fmt.Println(contact)
}
Both Address and Contact implement Stringer. The Contact's String() method uses Address's String() method to build its output. This creates a clean, hierarchical format.
Stringer with collections
Stringer can format collection types like slices and maps. This example shows custom formatting for a slice type.
package main
import (
"fmt"
"strconv"
)
type IntList []int
func (list IntList) String() string {
var builder strings.Builder
builder.WriteString("[")
for i, v := range list {
if i > 0 {
builder.WriteString(", ")
}
builder.WriteString(strconv.Itoa(v))
}
builder.WriteString("]")
return builder.String()
}
func main() {
nums := IntList{1, 2, 3, 4, 5}
fmt.Println(nums) // Prints "[1, 2, 3, 4, 5]"
}
The IntList type implements Stringer to format its elements. The strings.Builder is used for efficient string concatenation. This produces clean output for slices.
Stringer with enums
Stringer is commonly used with iota-based enums to provide readable names. This example demonstrates enum string representation.
package main
import "fmt"
type Status int
const (
Pending Status = iota
Processing
Completed
Failed
)
func (s Status) String() string {
switch s {
case Pending:
return "Pending"
case Processing:
return "Processing"
case Completed:
return "Completed"
case Failed:
return "Failed"
default:
return fmt.Sprintf("Status(%d)", s)
}
}
func main() {
status := Processing
fmt.Println("Current status:", status)
}
The Status type implements Stringer to return descriptive names for enum values. This makes output more readable than raw integer values.
Stringer with embedded types
Stringer can be implemented for types that embed other types. This example shows how embedded types affect string representation.
package main
import "fmt"
type Point struct {
X, Y int
}
func (p Point) String() string {
return fmt.Sprintf("(%d,%d)", p.X, p.Y)
}
type Circle struct {
Point // Embedded
Radius int
}
func (c Circle) String() string {
return fmt.Sprintf("Circle at %s with radius %d", c.Point, c.Radius)
}
func main() {
c := Circle{Point{10, 20}, 5}
fmt.Println(c) // Uses Circle's String() method
}
Circle embeds Point and both implement Stringer. Circle's String() method uses Point's String() method in its output. This creates a clean, hierarchical format.
Stringer with error handling
Stringer implementations can include error handling for invalid states. This example shows robust string formatting.
package main
import (
"fmt"
"time"
)
type Event struct {
Name string
Timestamp time.Time
}
func (e Event) String() string {
if e.Timestamp.IsZero() {
return fmt.Sprintf("%s (no time set)", e.Name)
}
return fmt.Sprintf("%s at %s", e.Name, e.Timestamp.Format(time.RFC3339))
}
func main() {
e1 := Event{"Meeting", time.Now()}
e2 := Event{"Party", time.Time{}} // Zero time
fmt.Println(e1)
fmt.Println(e2)
}
The Event type's String() method checks for zero time values. It provides different output formats based on the object's state. This makes output more informative.
Source
This tutorial covered the fmt.Stringer interface in Go with practical
examples of custom string representation for various types.
Author
List all Golang tutorials.