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.