Thuta Learning
ExercisesProgrammingbeginner

Exercise Set 2: Struct, Protocol & Error Handling Practice

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

What you'll walk away with

  • Work through Exercise Set 2: Struct, Protocol & Error Handling Practice on your own
  • Practice the skills you've already learned to solidify them
  • Get better at finding bugs, fixing them, and checking your own work

Let's think about this for a second

This exercise set is harder than Exercise Set 1, and it's meant to combine practice on the protocol, closure, enum with associated value, and error handling topics from the Intermediate/Advanced chapter. It's not new teaching content — it's a self-test where you apply the concepts by writing code yourself. The tasks are independent of the Task Manager project, designed so you can practice each concept in isolation. After solving each one, read any compile errors yourself and try to fix them.

Exercises

Task 1: Build a Shape protocol with a var area: Double { get } requirement and make Circle and Rectangle structs conform to it. Task 2: Build a [Circle] array and use closure-based sorted(by:) to sort it from smallest to largest area. Task 3: Build a NetworkResult enum with two associated values, success(data: String) and failure(reason: String), and handle both cases with a switch statement. Task 4: Write a validateAge(_ age: Int) throws -> Int function that throws a custom Error enum (InvalidAgeError) if age is below 0 or above 120, and handle it with do-catch.

Sample Code

swift
// Task 1
protocol Shape {
    var area: Double { get }
}

struct Circle: Shape {
    let radius: Double
    var area: Double {
        // TODO: implement
        return 0
    }
}

struct Rectangle: Shape {
    let width: Double
    let height: Double
    var area: Double {
        // TODO: implement
        return 0
    }
}

// Task 2
let circles = [Circle(radius: 3), Circle(radius: 1), Circle(radius: 2)]
// TODO: sorted(by:) closure

// Task 3
enum NetworkResult {
    case success(data: String)
    case failure(reason: String)
}
// TODO: switch statement

// Task 4
enum InvalidAgeError: Error {
    case outOfRange
}
func validateAge(_ age: Int) throws -> Int {
    // TODO: implement
    return age
}
You should see
The Circle/Rectangle area calculations should be correct, the circles array should be sorted by radius order (1, 2, 3), and calling validateAge(150) should have the catch block correctly catch the InvalidAgeError.outOfRange error.

Give it 5 minutes

Add a loading case to the NetworkResult enum from Task 3 and update the switch statement to stay exhaustive. Try it yourself for about 5 minutes.

One thing to watch out for

When switching over an enum with associated values, you need to cover every case — reaching for a default case too easily means the compiler won't warn you when a new case gets added later, and you can end up with a silent logic bug.

Easy traps

  • Declaring a protocol requirement as { get } but implementing it as a stored property instead of adding any computed property logic
  • When catching an error from a throws function, using a single general catch { } instead of switch-style pattern matching (catch InvalidAgeError.outOfRange), losing the error detail

Try It Yourself

Add a loading case to the NetworkResult enum from Task 3 and update the switch statement to stay exhaustive. Try it yourself for about 5 minutes.

You'll know it worked when: The Circle/Rectangle area calculations should be correct, the circles array should be sorted by radius order (1, 2, 3), and calling validateAge(150) should have the catch block correctly catch the InvalidAgeError.outOfRange error.

Exercise Set 2: Struct, Protocol & Error Handling Practice | Thuta Learning