Golang fmt.GoStringer Interface
last modified May 8, 2025
This tutorial explains how to use the fmt.GoStringer
interface in Go.
We'll cover interface basics with practical examples of custom Go syntax output.
The GoStringer interface is used to customize the Go syntax representation of values. It's similar to Stringer but used with the %#v format verb.
In Go, GoStringer
is implemented by types that want to control their
representation when printed with %#v. This is useful for debugging and testing.
Basic GoStringer implementation
The simplest use of GoStringer
provides custom Go syntax output.
This example demonstrates basic interface implementation.
Note: The %#v verb triggers GoString() method calls.
package main import "fmt" type Point struct { X, Y int } func (p Point) GoString() string { return fmt.Sprintf("Point{X: %d, Y: %d}", p.X, p.Y) } func main() { p := Point{X: 10, Y: 20} fmt.Printf("Regular: %v\n", p) fmt.Printf("Go syntax: %#v\n", p) }
The Point type implements GoStringer to provide custom Go syntax output. %v shows default format while %#v uses our GoString() method.
GoStringer with private fields
GoStringer can expose private fields in a controlled way. This example shows how to represent structs with unexported fields using GoString().
package main import "fmt" type secretData struct { public string private string } func (s secretData) GoString() string { return fmt.Sprintf("secretData{public: %q, private: \"****\"}", s.public) } func main() { data := secretData{ public: "visible", private: "hidden", } fmt.Printf("%#v\n", data) }
The GoString() method hides the private field while showing the public one. This maintains encapsulation while providing debug information.
GoStringer for custom collections
Implementing GoStringer for collection types improves debugging output. This example shows a custom list type with GoString() implementation.
package main import ( "fmt" "strings" ) type StringList []string func (sl StringList) GoString() string { var builder strings.Builder builder.WriteString("StringList{") for i, s := range sl { if i > 0 { builder.WriteString(", ") } builder.WriteString(fmt.Sprintf("%q", s)) } builder.WriteString("}") return builder.String() } func main() { list := StringList{"apple", "banana", "cherry"} fmt.Printf("%#v\n", list) }
The StringList type implements GoStringer to show its contents clearly. The output matches valid Go syntax for initializing the collection.
GoStringer with pointer receivers
GoStringer can be implemented with pointer receivers for mutable types. This example demonstrates pointer receiver implementation.
package main import "fmt" type Counter struct { value int } func (c *Counter) Increment() { c.value++ } func (c *Counter) GoString() string { return fmt.Sprintf("&Counter{value: %d}", c.value) } func main() { c := &Counter{value: 5} fmt.Printf("%#v\n", c) c.Increment() fmt.Printf("%#v\n", c) }
The Counter type uses a pointer receiver for GoString() to match its methods. This maintains consistency in the type's representation.
GoStringer for interface values
GoStringer implementations can help debug interface values. This example shows how to implement GoStringer for interface types.
package main import "fmt" type Shape interface { Area() float64 GoString() string } type Circle struct { Radius float64 } func (c Circle) Area() float64 { return 3.14 * c.Radius * c.Radius } func (c Circle) GoString() string { return fmt.Sprintf("Circle{Radius: %g}", c.Radius) } func printShape(s Shape) { fmt.Printf("%#v\n", s) } func main() { c := Circle{Radius: 5.0} printShape(c) }
The Shape interface includes GoStringer, ensuring all implementations provide debug output. This helps when working with interface values.
Combining Stringer and GoStringer
Types can implement both Stringer and GoStringer for different outputs. This example shows a type with both interfaces implemented.
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 (p Person) GoString() string { return fmt.Sprintf("Person{Name: %q, Age: %d}", p.Name, p.Age) } func main() { p := Person{Name: "Alice", Age: 30} fmt.Println(p) // Uses String() fmt.Printf("%v\n", p) // Uses String() fmt.Printf("%#v\n", p) // Uses GoString() }
The Person type provides different outputs for %v and %#v format verbs. String() is for human-readable output, GoString() for Go syntax.
GoStringer with nested structs
GoStringer implementations can handle nested structs recursively. This example demonstrates GoString() for a complex structure.
package main import "fmt" type Address struct { Street string City string Country string } func (a Address) GoString() string { return fmt.Sprintf("Address{Street: %q, City: %q, Country: %q}", a.Street, a.City, a.Country) } type User struct { Name string Age int Address Address } func (u User) GoString() string { return fmt.Sprintf("User{Name: %q, Age: %d, Address: %#v}", u.Name, u.Age, u.Address) } func main() { user := User{ Name: "Bob", Age: 40, Address: Address{ Street: "123 Main St", City: "Springfield", Country: "USA", }, } fmt.Printf("%#v\n", user) }
Both User and Address implement GoStringer for complete representation. The nested Address is properly formatted using %#v recursively.
Source
This tutorial covered the fmt.GoStringer
interface in Go with
practical examples of custom Go syntax representation for various types.
Author
List all Golang tutorials.