IntermediateInterfaces & errors
Lab: interfaces, assertions, and errors
Scenario questions that test interface satisfaction, type assertions, and error wrapping patterns in Go.
Lab · optionalGoIntermediate15 min
Recommended first
By the end of this lesson you will be able to:
- Identify whether a type satisfies a given interface
- Predict the behaviour of type assertions and type switches
- Trace an error chain built with %w and Unwrap
Optional lab. These scenario questions reinforce the ideas from the Interfaces and errors module. Work through them in order — each builds on the previous — and check the explanations carefully when you're wrong.
Interface satisfaction scenarios
Knowledge check
- 1.You define:
type Printer interface { Print() }
type Doc struct{ Text string }
func (d Doc) Print() { fmt.Println(d.Text) }
Which assignment compiles? - 2.You change Print() to use a pointer receiver: func (d *Doc) Print(). Now which assignment compiles?
- 3.Every Go type satisfies the empty interface interface{}.
Type assertion and type switch scenarios
Knowledge check
- 1.var i interface{} = "hello"
n := i.(int) // single-return form
What happens at runtime? - 2.In a type switch, what does the default case match?
Error wrapping scenarios
Knowledge check
- 1.Consider:
var ErrTimeout = errors.New("timeout")
inner := fmt.Errorf("db query: %w", ErrTimeout)
outer := fmt.Errorf("handler: %w", inner)
What does errors.Is(outer, ErrTimeout) return? - 2.You want to check whether an error chain contains a *ValidationError and retrieve its Field. Which function do you use?
- 3.Using fmt.Errorf("context: %v", err) preserves the error chain so errors.Is can find the original error.
Putting it together
Here is a pattern you will see in production Go code. Read it and make sure each piece makes sense:
var ErrNotFound = errors.New("not found")
type StoreError struct {
Op string
Err error
}
func (e *StoreError) Error() string {
return fmt.Sprintf("store.%s: %v", e.Op, e.Err)
}
func (e *StoreError) Unwrap() error {
return e.Err
}
func getUser(id int) (*User, error) {
u := db[id]
if u == nil {
return nil, &StoreError{Op: "GetUser", Err: ErrNotFound}
}
return u, nil
}
// In the caller:
err := getUser(42)
if errors.Is(err, ErrNotFound) {
// respond with 404
}
var se *StoreError
if errors.As(err, &se) {
log.Printf("store op failed: %s", se.Op)
}The StoreError adds structured context (Op) while keeping the sentinel ErrNotFound reachable through the chain. This is idiomatic Go error design.
Where to go next
The next module, Packages & testing, covers how Go organises code at scale — package design, modules, and writing tests that give you confidence to refactor.
Finished reading? Mark it complete to track your progress.