Menu
Coddy logo textTech

Stringer & Error Interfaces

Part of the Object Oriented Programming section of Coddy's GO journey — lesson 32 of 107.

Go's standard library defines two interfaces that you'll use constantly: fmt.Stringer and error. Both are simple single-method interfaces that give your types powerful integration with Go's built-in functionality.

The Stringer interface lives in the fmt package and requires just one method:

type Stringer interface {
    String() string
}

When you implement String() on your type, functions like fmt.Println automatically use it to display your value:

type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}

func main() {
    p := Person{Name: "Alice", Age: 30}
    fmt.Println(p)  // Alice (30 years old)
}

The error interface is equally simple, requiring an Error() method that returns a string:

type error interface {
    Error() string
}

Any type with an Error() method can be used as an error value in Go. This lets you create custom error types with additional context:

type ValidationError struct {
    Field   string
    Message string
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("%s: %s", e.Field, e.Message)
}

func main() {
    err := ValidationError{Field: "email", Message: "invalid format"}
    fmt.Println(err)  // email: invalid format
}

These two interfaces demonstrate Go's philosophy: small, focused interfaces that unlock powerful behavior through implicit implementation.

challenge icon

Challenge

Easy

Let's build a temperature monitoring system that uses both the Stringer and error interfaces to provide readable output and meaningful error handling.

You'll organize your code across two files:

  • temperature.go: Create a Temperature struct with Value (float64) and Unit (string) fields. Implement the String() method so that when printed, it displays the temperature in a human-readable format. Also create a TemperatureError struct with Temp (float64) and Reason (string) fields, and implement the Error() method to provide descriptive error messages.
  • main.go: Create a function called ValidateTemperature that takes a Temperature and returns an error if the temperature is invalid. A temperature is invalid if it's below absolute zero (-273.15 for Celsius, -459.67 for Fahrenheit). If valid, the function returns nil. Read temperature data from input, create a Temperature, validate it, and print appropriate output.

The following inputs will be provided:

  • Line 1: Temperature value (float)
  • Line 2: Unit (C for Celsius or F for Fahrenheit)

Your String() method should return the format:

[Value] degrees [Unit]

Where Unit is the full word "Celsius" or "Fahrenheit" based on the input.

Your Error() method should return the format:

invalid temperature [Temp]: [Reason]

If the temperature is valid, print the Temperature using fmt.Println (which will use your String() method). If invalid, print the error (which will use your Error() method).

For example, given 25.5 and C, your output should be:

25.5 degrees Celsius

Given -500 and F, your output should be:

invalid temperature -500: below absolute zero

Use "below absolute zero" as the reason when the temperature is too low.

Cheat sheet

Go's standard library defines two essential interfaces: fmt.Stringer and error.

The Stringer Interface

The Stringer interface from the fmt package requires one method:

type Stringer interface {
    String() string
}

When you implement String() on your type, functions like fmt.Println automatically use it:

type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}

func main() {
    p := Person{Name: "Alice", Age: 30}
    fmt.Println(p)  // Alice (30 years old)
}

The error Interface

The error interface requires an Error() method that returns a string:

type error interface {
    Error() string
}

Any type with an Error() method can be used as an error value, allowing custom error types with additional context:

type ValidationError struct {
    Field   string
    Message string
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("%s: %s", e.Field, e.Message)
}

func main() {
    err := ValidationError{Field: "email", Message: "invalid format"}
    fmt.Println(err)  // email: invalid format
}

Try it yourself

package main

import (
	"fmt"
)

// ValidateTemperature checks if a temperature is valid
// Returns an error if the temperature is below absolute zero
// Absolute zero: -273.15 for Celsius, -459.67 for Fahrenheit
func ValidateTemperature(t Temperature) error {
	// TODO: Implement validation logic
	// Check if temperature is below absolute zero based on unit
	// Return a TemperatureError if invalid, nil if valid
	return nil
}

func main() {
	var value float64
	var unit string
	fmt.Scanln(&value)
	fmt.Scanln(&unit)

	// TODO: Create a Temperature struct with the input values

	// TODO: Validate the temperature using ValidateTemperature

	// TODO: If there's an error, print the error
	// Otherwise, print the temperature (which will use String() method)
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming