fmt.Stringer Interface
Part of the Object Oriented Programming section of Coddy's GO journey — lesson 75 of 107.
The fmt.Stringer interface controls how your types appear when printed. It's one of the most commonly implemented interfaces in Go, defined in the fmt package:
type Stringer interface {
String() string
}When you pass a value to fmt.Println, fmt.Printf with %v, or similar functions, Go checks if the type implements Stringer. If it does, Go calls the String() method to get the text representation.
type Temperature struct {
Celsius float64
}
func (t Temperature) String() string {
return fmt.Sprintf("%.1f°C", t.Celsius)
}
func main() {
temp := Temperature{Celsius: 23.5}
fmt.Println(temp) // 23.5°C
}Without the String() method, printing would show the default struct format: {23.5}. By implementing Stringer, you control exactly how your type presents itself.
This is particularly useful for types where the raw field values don't convey meaning clearly. A Duration type might display as "2h 30m" instead of showing raw nanoseconds, or a User type might show a formatted name instead of all its fields.
Challenge
EasyLet's build a time tracking system that displays durations in a human-friendly format! You'll implement the fmt.Stringer interface to control how your custom types appear when printed.
You'll organize your code across two files:
duration.go: Define your time-related types with custom string representations.Create a
Durationstruct that stores a total number of minutes as an integer. Implement theString()method so that when printed, it displays in a readable format like2h 30mfor 150 minutes, or0h 45mfor 45 minutes. Hours and minutes should always be shown.Create a
Taskstruct with fields forName(string) andTime(Duration). ImplementString()on Task to display in the format[Name]: [duration], where the duration uses its own String method.main.go: Build and display tasks with their durations.Read a count of tasks, then for each task read its name and duration in minutes. Create Task instances and print each one directly using
fmt.Println—the Stringer interface will handle the formatting automatically.After printing all tasks, calculate and print the total time across all tasks in the format:
Total: [duration]
The following inputs will be provided:
- Line 1: Number of tasks
- Following lines: Task name and minutes (two lines per task)
For example, given:
3
Code Review
90
Meeting
45
Documentation
120Your output should be:
Code Review: 1h 30m
Meeting: 0h 45m
Documentation: 2h 0m
Total: 4h 15mAnd given:
2
Design
180
Testing
65Your output should be:
Design: 3h 0m
Testing: 1h 5m
Total: 4h 5mBy implementing Stringer, your types integrate seamlessly with Go's printing functions. Instead of seeing raw struct fields like {90}, you get meaningful output that makes sense to users.
Cheat sheet
The fmt.Stringer interface controls how types appear when printed:
type Stringer interface {
String() string
}When you pass a value to fmt.Println or fmt.Printf with %v, Go checks if the type implements Stringer. If it does, Go calls the String() method to get the text representation.
Example implementation:
type Temperature struct {
Celsius float64
}
func (t Temperature) String() string {
return fmt.Sprintf("%.1f°C", t.Celsius)
}
func main() {
temp := Temperature{Celsius: 23.5}
fmt.Println(temp) // 23.5°C
}Without the String() method, printing shows the default struct format: {23.5}. By implementing Stringer, you control exactly how your type presents itself, making output more meaningful and user-friendly.
Try it yourself
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Read number of tasks
line, _ := reader.ReadString('\n')
numTasks, _ := strconv.Atoi(strings.TrimSpace(line))
// TODO: Create a slice to store tasks
// TODO: Track total minutes
for i := 0; i < numTasks; i++ {
// Read task name
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
// Read task duration in minutes
minutesLine, _ := reader.ReadString('\n')
minutes, _ := strconv.Atoi(strings.TrimSpace(minutesLine))
// TODO: Create a Task with the name and Duration
// TODO: Print the task (fmt.Println will use the Stringer interface)
// TODO: Add minutes to total
_ = name
_ = minutes
}
// TODO: Print the total duration in format "Total: Xh Ym"
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser11Standard Library & OOP
io.Reader & io.Writersort.Interfacefmt.Stringer Interfaceencoding/json with Structshttp.Handler InterfaceRecap - REST API Models3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool