# Exercises

### Exercises

1. Write a function `parseUpper` that accepts a byte and returns the uppercase letter value. Return `error.InvalidUppercase` if the byte is not between `'A'` and `'Z'`.

```zig
fn parseUpper(c: u8) !u8 {
    // ...
}
```

2. Write a function `safeDivide` that divides two integers. Return `error.DivisionByZero` when the divisor is zero.

```zig
fn safeDivide(a: u32, b: u32) !u32 {
    // ...
}
```

3. Write a function `first` that returns the first byte of a slice. Return `error.EmptySlice` if the slice is empty.

```zig
fn first(s: []const u8) !u8 {
    // ...
}
```

4. Write a function `last` that returns the last byte of a slice.

5. Write a function `requirePositive` that returns `error.NegativeNumber` if its argument is less than zero.

6. Rewrite this code using `try`:

```zig
const value = parseNumber(text) catch |err| return err;
```

7. Rewrite this code using `catch`:

```zig
const value = try parseNumber(text);
```

8. Write a function `parseTwoDigits` that parses two ASCII digits into a number between `0` and `99`.

```zig
try parseTwoDigits('4', '2') // 42
```

9. Write a function that opens a file and closes it with `defer`.

10. Allocate memory with an allocator and release it with `defer`.

11. Allocate memory and return it from a function. Use `errdefer` so that memory is freed only on failure.

12. Write a function that translates `error.FileNotFound` into `error.MissingConfig`.

13. Write a program that reads a filename from the command line and prints a clear error message if opening the file fails.

14. Write a parser that never prints errors directly. Handle all printing in `main`.

15. Write a function that parses an integer from a byte slice and returns:

| Error | Meaning |
|---|---|
| `error.EmptyInput` | slice is empty |
| `error.InvalidDigit` | non-digit found |
| `error.Overflow` | value does not fit |

16. Write a function that reads a file into memory using:

- `readFileAlloc`
- `defer`
- `try`
- `catch`

17. Write a function that creates two resources and releases them in reverse order using multiple `defer` statements.

18. Modify a previous program so that all resource cleanup is handled by `defer` and all error propagation uses `try`.

19. Write a small command-line calculator:

```text
calc 10 / 2
5

calc 10 / 0
error: DivisionByZero
```

20. Write a program that loads a configuration file and applies these rules:

| Failure | Behavior |
|---|---|
| file missing | use defaults |
| invalid config | print error and stop |
| out of memory | return error immediately |

21. Write a function that retries a failing operation three times before returning the final error.

22. Write a small library with public functions that return explicit error sets.

23. Write a wrapper function that converts low-level filesystem errors into application-level errors.

24. Review a previous chapter program and identify:

- where `try` should be used
- where `catch` should be used
- where `defer` belongs
- where `errdefer` belongs
- where the program boundary should handle errors

25. Write a complete program that:

- reads a file
- parses integers from it
- allocates memory dynamically
- handles malformed input
- frees all resources correctly
- prints useful error messages only at the outer boundary

This chapter introduced four central ideas:

| Feature | Purpose |
|---|---|
| error sets | describe failure |
| error unions | combine values and errors |
| `try` | propagate failure |
| `catch` | handle failure |
| `defer` | guaranteed cleanup |
| `errdefer` | cleanup only on error |

These appear throughout Zig programs. Most library APIs use them directly, and most real programs depend on them for resource management and control flow.

