Thuta Learning
ExercisesProgrammingbeginner

Exercises: Interfaces & Concurrency Challenge

Relax. We'll talk through this in plain words — no textbook voice.

What you'll walk away with

  • Practice Exercises: Interfaces & Concurrency Challenge on your own
  • Practice the skills you've already learned to make them stick
  • Learn to find bugs, fix them, and check your own work

Let's think about this for a second

This lesson is tougher than the first Exercises lesson, combining the interface, custom error type, goroutine, and channel concepts you learned in the Advanced chapter into a real challenge. Each task reflects a pattern you'll commonly run into in real-world Go programs, giving you hands-on practice with polymorphism, safe error handling, and concurrent processing. Finishing this practice set will help you extend the Task Manager project from the Projects chapter on your own.

Exercises

Task 1: Define a Shape interface with an Area() float64 method. Create two structs, Circle{Radius float64} and Rectangle{Width, Height float64}, implement the Area() method for both, and run them together in a Shape slice. Task 2: Create a custom error type called InvalidAgeError (with an Error() string method), then write a validateAge(age int) error function that returns this custom error if age is less than 0 or greater than 150. Task 3: Using the worker pool pattern, write a program where a channel receives 10 numbers, 3 goroutines run in parallel, each squares its number, and sends the result back through a result channel.

Code Example

go
package main

import (
	"fmt"
	"sync"
)

// Task 1
type Shape interface {
	Area() float64
}

type Circle struct{ Radius float64 }
type Rectangle struct{ Width, Height float64 }

func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }
func (r Rectangle) Area() float64 { return r.Width * r.Height }

// Task 2
type InvalidAgeError struct{ Age int }

func (e *InvalidAgeError) Error() string {
	return fmt.Sprintf("invalid age: %d", e.Age)
}

func validateAge(age int) error {
	if age < 0 || age > 150 {
		return &InvalidAgeError{Age: age}
	}
	return nil
}

// Task 3
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
	defer wg.Done()
	for n := range jobs {
		results <- n * n
	}
}

func main() {
	shapes := []Shape{Circle{Radius: 2}, Rectangle{Width: 3, Height: 4}}
	for _, s := range shapes {
		fmt.Println("Area:", s.Area())
	}

	if err := validateAge(200); err != nil {
		fmt.Println("Error:", err)
	}

	jobs := make(chan int, 10)
	results := make(chan int, 10)
	var wg sync.WaitGroup

	for w := 1; w <= 3; w++ {
		wg.Add(1)
		go worker(w, jobs, results, &wg)
	}
	for n := 1; n <= 10; n++ {
		jobs <- n
	}
	close(jobs)
	wg.Wait()
	close(results)

	for r := range results {
		fmt.Println("squared:", r)
	}
}
You should see
It will print the Area() values for Circle and Rectangle, show a custom error message for age 200, and print 10 square values (in no particular order) for numbers 1-10 from the worker pool.

Try it in 5 minutes

Within 5 minutes, change the worker count from 3 to 5 and observe how the output order changes, so you can see for yourself that goroutine scheduling isn't deterministic.

One quick word of caution

When running lots of goroutines, directly modifying a shared variable can cause a data race, so you should follow the Go idiom of communicating through channels instead - "don't communicate by sharing memory; share memory by communicating."

Easy traps

  • Defining InvalidAgeError's Error() method with a pointer receiver (*InvalidAgeError), but then trying to return it as a value (InvalidAgeError{...}) from validateAge(), so it no longer satisfies the interface
  • Never calling close(jobs) in the worker pool, so the worker goroutines stay blocked forever in the range jobs loop and wg.Wait() never returns, causing a deadlock

Now try it yourself

Within 5 minutes, change the worker count from 3 to 5 and observe how the output order changes, so you can see for yourself that goroutine scheduling isn't deterministic.

You'll know it worked when: It will print the Area() values for Circle and Rectangle, show a custom error message for age 200, and print 10 square values (in no particular order) for numbers 1-10 from the worker pool.

Exercises: Interfaces & Concurrency Challenge | Thuta Learning