The Stringer Interface
Customize how types print.
The Stringer Interface is a free Go Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Go Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Default Struct Printing
By default, printing a struct with %v shows its raw fields. That is fine for debugging but not always reader-friendly.
package main
import "fmt"
type Color struct{ R, G, B int }
func main() {
c := Color{255, 0, 0}
fmt.Println(c)
}The Stringer Interface
The fmt.Stringer interface has one method:
String() string
Any type that defines it controls how it appears when printed.
package main
import "fmt"
type Greeting struct{ Name string }
func (g Greeting) String() string {
return "Hello, " + g.Name
}
func main() {
fmt.Println(Greeting{"Ada"})
}Used Automatically
When a type has a String() method, Println, Printf with %v or %s, and friends all call it automatically.
package main
import "fmt"
type Greeting struct{ Name string }
func (g Greeting) String() string {
return "Hi " + g.Name
}
func main() {
g := Greeting{"Bob"}
fmt.Printf("%s and %v\n", g, g)
}Formatting Inside String()
Build the result with fmt.Sprintf to produce a clean, custom representation.
package main
import "fmt"
type Color struct{ R, G, B int }
func (c Color) String() string {
return fmt.Sprintf("rgb(%d, %d, %d)", c.R, c.G, c.B)
}
func main() {
fmt.Println(Color{255, 128, 0})
}Stringer on Named Types
Stringer is not just for structs. Any named type, even one based on int, can implement it. This is the classic enum pattern.
package main
import "fmt"
type Day int
func (d Day) String() string {
names := []string{"Mon", "Tue", "Wed"}
return names[d]
}
func main() {
fmt.Println(Day(1))
}Enums That Print Nicely
Combine iota with a Stringer so constants print as readable names instead of numbers.
package main
import "fmt"
type State int
const (
Idle State = iota
Running
Done
)
func (s State) String() string {
return []string{"Idle", "Running", "Done"}[s]
}
func main() {
fmt.Println(Running)
}Value vs Pointer Receiver
If String() uses a value receiver, both values and pointers print nicely. With a pointer receiver, only pointers trigger it.
package main
import "fmt"
type Tag struct{ Name string }
func (t Tag) String() string { return "#" + t.Name }
func main() {
t := Tag{"go"}
fmt.Println(t)
fmt.Println(&t)
}Avoid Infinite Recursion
Never call fmt.Sprintf("%v", t) on the same type inside its own String(). That calls String() again forever. Format the fields directly.
package main
import "fmt"
type Money struct{ Cents int }
func (m Money) String() string {
return fmt.Sprintf("$%.2f", float64(m.Cents)/100)
}
func main() {
fmt.Println(Money{1050})
}Stringer in Slices
When you print a slice of Stringer values, each element is formatted with its String() method.
package main
import "fmt"
type Tag struct{ Name string }
func (t Tag) String() string { return "#" + t.Name }
func main() {
tags := []Tag{{"go"}, {"web"}}
fmt.Println(tags)
}Using It as an Interface
You can store any Stringer in a fmt.Stringer variable and call String() through the interface.
package main
import "fmt"
type City struct{ Name string }
func (c City) String() string { return "City of " + c.Name }
func main() {
var s fmt.Stringer = City{"Oslo"}
fmt.Println(s.String())
}Why It Matters
Implementing Stringer gives your types clean output everywhere they are printed or logged, with no extra formatting code at each call site.
package main
import "fmt"
type Temp float64
func (t Temp) String() string {
return fmt.Sprintf("%.1f°C", float64(t))
}
func main() {
fmt.Println(Temp(21.5))
}Quick Check
What method must a type define to satisfy fmt.Stringer?
Recap: The Stringer Interface
You learned to customize printing:
- Implement
String() stringto satisfyfmt.Stringer. fmtcalls it automatically for%vand%s.- Great for enums; avoid recursive
%von the same type.
package main
import "fmt"
type Level int
func (l Level) String() string {
return []string{"low", "mid", "high"}[l]
}
func main() {
fmt.Println(Level(2))
}Frequently asked questions
Is the “The Stringer Interface” lesson free?
Yes — the full text of “The Stringer Interface” is free to read here on the web, and the Go Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Go Academy course, upgrade to CoddyKit PRO.
What will I learn in “The Stringer Interface”?
Customize how types print. You practise Go Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Go Academy?
No prior experience is required. Go Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “The Stringer Interface” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Go Academy lesson?
Yes. Every Go Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Printf Verbs
- Sprintf and Fprintf
- The Stringer Interface
- Scanning Input