# Exercises

### Exercises

Exercise 9-17. Declare an optional integer.

```zig
const x: ?i32 = 10;
```

Change it to `null`.

```zig
const x: ?i32 = null;
```

Print both forms with `{any}`.

Exercise 9-18. Write a function `firstPositive` that returns the first positive number in a slice.

```zig
fn firstPositive(values: []const i32) ?i32 {
    for (values) |v| {
        if (v > 0) {
            return v;
        }
    }

    return null;
}
```

Test it with:

```zig
const a = [_]i32{ -3, -2, 0, 5, 9 };
const b = [_]i32{ -3, -2, 0 };
```

Exercise 9-19. Write a function `indexOf` that returns the index of a byte in a string.

```zig
fn indexOf(s: []const u8, target: u8) ?usize {
    for (s, 0..) |c, i| {
        if (c == target) {
            return i;
        }
    }

    return null;
}
```

Use it like this:

```zig
const pos = indexOf("hello", 'l');

if (pos) |i| {
    std.debug.print("found at {d}\n", .{i});
} else {
    std.debug.print("not found\n", .{});
}
```

Exercise 9-20. Rewrite this code without force unwrapping.

```zig
const x: ?i32 = 7;
const y = x.?;
std.debug.print("{d}\n", .{y});
```

Use optional capture:

```zig
if (x) |y| {
    std.debug.print("{d}\n", .{y});
}
```

Exercise 9-21. Write a function `valueOr` that takes an optional integer and a default value.

```zig
fn valueOr(x: ?i32, default: i32) i32 {
    return x orelse default;
}
```

Test:

```zig
std.debug.print("{d}\n", .{valueOr(10, 0)});
std.debug.print("{d}\n", .{valueOr(null, 0)});
```

Exercise 9-22. Write a function that returns a pointer to the first zero in a mutable slice.

```zig
fn firstZero(values: []i32) ?*i32 {
    for (values) |*v| {
        if (v.* == 0) {
            return v;
        }
    }

    return null;
}
```

Use the returned pointer to change the zero to `100`.

```zig
var values = [_]i32{ 3, 2, 0, 8 };

if (firstZero(&values)) |p| {
    p.* = 100;
}
```

Exercise 9-23. Write a function that returns the last element of a slice.

```zig
fn last(values: []const i32) ?i32 {
    if (values.len == 0) {
        return null;
    }

    return values[values.len - 1];
}
```

Exercise 9-24. Decide whether each return type should be optional or error union.

```text
find a user by id
open a socket
look up an environment variable
allocate memory
find a substring
parse a JSON document
```

Use optional for normal absence. Use an error union for failed operations.

Exercise 9-25. Combine errors and optionals.

Write a function declaration for loading a key from a config file.

The file operation may fail.

The key may be absent.

A suitable return type is:

```zig
fn loadConfigValue(path: []const u8, key: []const u8) !?[]const u8
```

The caller should handle it in two steps:

```zig
const value = try loadConfigValue("app.conf", "port");

if (value) |v| {
    std.debug.print("port = {s}\n", .{v});
} else {
    std.debug.print("port not set\n", .{});
}
```

Exercise 9-26. Write a linked list traversal using optional pointers.

```zig
const Node = struct {
    value: i32,
    next: ?*Node,
};
```

Start with:

```zig
var current: ?*Node = &head;
```

Loop until `current` becomes `null`.

```zig
while (current) |node| {
    std.debug.print("{d}\n", .{node.value});
    current = node.next;
}
```

Exercise 9-27. Explain why `?*T` and `*T` are different types.

Exercise 9-28. Explain why `?usize` is better than returning `usize` with a special value like `0` or `maxInt(usize)` for “not found.”

