# Optional Pointers

### Optional Pointers

An optional pointer is a pointer that may have no value.

In Zig, a normal pointer must point to something valid. It cannot be `null`.

```zig
var x: i32 = 10;
const p: *i32 = &x;
```

Here, `p` points to `x`.

This is not allowed:

```zig
const p: *i32 = null;
```

A normal pointer says:

```text
I point to a valid i32.
```

But sometimes a pointer needs to represent absence. For that, Zig uses an optional pointer.

```zig
?*T
```

Read this as:

```text
optional pointer to T
```

For example:

```zig
?*i32
```

means:

```text
either a pointer to an i32, or null
```

#### Why Optional Pointers Exist

Many programs need to express “found” or “not found.”

Suppose we search for a number in a small array. If we find it, we want to return a pointer to the matching item. If we do not find it, we need to return “nothing.”

That is exactly what an optional pointer represents.

```zig
fn findFirst(values: []i32, target: i32) ?*i32 {
    for (values) |*value| {
        if (value.* == target) {
            return value;
        }
    }

    return null;
}
```

The return type is:

```zig
?*i32
```

That means the function may return a pointer to an `i32`, or it may return `null`.

The caller must check before using the pointer.

#### Null Is Explicit

In some languages, every pointer or object reference can be null. This causes many runtime errors because any access may fail unexpectedly.

Zig takes a stricter approach.

A normal pointer cannot be null:

```zig
*i32
```

An optional pointer can be null:

```zig
?*i32
```

This difference is visible in the type.

That means when you see this:

```zig
fn update(value: *i32) void
```

you know `value` is expected to point to a valid `i32`.

When you see this:

```zig
fn updateMaybe(value: ?*i32) void
```

you know the function must handle the possibility of `null`.

The type tells the truth.

#### Creating an Optional Pointer

You can assign a real pointer to an optional pointer:

```zig
var x: i32 = 10;
const maybe: ?*i32 = &x;
```

You can also assign `null`:

```zig
const maybe: ?*i32 = null;
```

Both are valid because `maybe` is optional.

A non-optional pointer cannot hold `null`.

```zig
var x: i32 = 10;

const good: ?*i32 = &x;
const empty: ?*i32 = null;

// const bad: *i32 = null; // error
```

This is the basic rule:

Use `*T` when the pointer must exist.

Use `?*T` when the pointer may be absent.

#### Unwrapping an Optional Pointer

You cannot directly dereference an optional pointer.

This is not valid:

```zig
const maybe: ?*i32 = &x;

// maybe.* = 20; // error
```

Why?

Because `maybe` might be `null`.

You must unwrap it first.

The most common way is `if`:

```zig
if (maybe) |p| {
    p.* = 20;
}
```

Inside the `if` block, `p` is a normal pointer.

If `maybe` contains a pointer, the block runs.

If `maybe` is `null`, the block does not run.

Full example:

```zig
const std = @import("std");

pub fn main() void {
    var x: i32 = 10;
    const maybe: ?*i32 = &x;

    if (maybe) |p| {
        p.* = 20;
    }

    std.debug.print("x = {}\n", .{x});
}
```

Output:

```text
x = 20
```

The optional pointer is checked before use.

#### Handling the Null Case

Often you need to handle both cases.

```zig
if (maybe) |p| {
    std.debug.print("value = {}\n", .{p.*});
} else {
    std.debug.print("no value\n", .{});
}
```

Complete program:

```zig
const std = @import("std");

pub fn main() void {
    const maybe: ?*i32 = null;

    if (maybe) |p| {
        std.debug.print("value = {}\n", .{p.*});
    } else {
        std.debug.print("no value\n", .{});
    }
}
```

Output:

```text
no value
```

This is one of Zig’s strengths. The `null` case is not hidden. The code must say what happens.

#### Optional Pointer Search Example

Here is a complete search example:

```zig
const std = @import("std");

fn findFirst(values: []i32, target: i32) ?*i32 {
    for (values) |*value| {
        if (value.* == target) {
            return value;
        }
    }

    return null;
}

pub fn main() void {
    var numbers = [_]i32{ 10, 20, 30, 40 };

    const result = findFirst(numbers[0..], 30);

    if (result) |p| {
        p.* = 99;
    }

    std.debug.print("{any}\n", .{numbers});
}
```

Output:

```text
{ 10, 20, 99, 40 }
```

The function returns a pointer to the matching element.

The caller modifies the element through that pointer.

Because the pointer points into the original array, the array changes.

#### Optional Pointer to Const

An optional pointer can also point to read-only data.

```zig
?*const T
```

For example:

```zig
?*const i32
```

means:

```text
either a pointer to a read-only i32, or null
```

Example:

```zig
fn findFirst(values: []const i32, target: i32) ?*const i32 {
    for (values) |*value| {
        if (value.* == target) {
            return value;
        }
    }

    return null;
}
```

The function receives:

```zig
[]const i32
```

So it cannot modify the array.

It returns:

```zig
?*const i32
```

So the caller can read the found value, but cannot modify it.

```zig
const std = @import("std");

fn findFirst(values: []const i32, target: i32) ?*const i32 {
    for (values) |*value| {
        if (value.* == target) {
            return value;
        }
    }

    return null;
}

pub fn main() void {
    const numbers = [_]i32{ 10, 20, 30, 40 };

    const result = findFirst(numbers[0..], 30);

    if (result) |p| {
        std.debug.print("found = {}\n", .{p.*});
    }
}
```

Output:

```text
found = 30
```

Use `?*const T` when the pointer may be missing and the value should only be read.

Use `?*T` when the pointer may be missing and the value may be modified.

#### Optional Pointer as a Field

Optional pointers are common in data structures.

For example, a linked list node may point to the next node. The last node has no next node.

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

The field:

```zig
next: ?*Node
```

means:

```text
this node may point to another node, or it may be the last node
```

Example:

```zig
const std = @import("std");

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

pub fn main() void {
    var third = Node{ .value = 30, .next = null };
    var second = Node{ .value = 20, .next = &third };
    var first = Node{ .value = 10, .next = &second };

    var current: ?*Node = &first;

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

Output:

```text
10
20
30
```

The loop continues while `current` contains a pointer. It stops when `current` becomes `null`.

This is a natural use of optional pointers.

#### Optional Pointers and Function Parameters

A function parameter should use a normal pointer when the pointer is required.

```zig
fn reset(value: *i32) void {
    value.* = 0;
}
```

This function should not accept `null`. It needs a real `i32`.

Use an optional pointer only when absence is meaningful.

```zig
fn resetMaybe(value: ?*i32) void {
    if (value) |p| {
        p.* = 0;
    }
}
```

This function says:

```text
If a value is provided, reset it. If not, do nothing.
```

Do not use optional pointers just because they seem flexible. They make every caller and every function body deal with `null`.

A stricter type is usually better.

#### Optional Pointers and Ownership

An optional pointer does not own memory.

This is still only a reference:

```zig
?*T
```

It means:

```text
maybe points to T
```

It does not mean:

```text
owns T
```

For example:

```zig
var x: i32 = 10;
const maybe: ?*i32 = &x;
```

The optional pointer points to `x`, but it does not own `x`. It must not outlive `x`.

The same lifetime rule applies:

A pointer is valid only while the memory it points to is valid.

Optionality does not change that.

#### Optional Pointer vs Optional Value

These two types are different:

```zig
?i32
?*i32
```

`?i32` means:

```text
either an i32 value, or null
```

`?*i32` means:

```text
either a pointer to an i32 somewhere else, or null
```

Example with optional value:

```zig
fn maybeNumber(ok: bool) ?i32 {
    if (ok) return 42;
    return null;
}
```

This returns the number itself.

Example with optional pointer:

```zig
fn maybePointer(ok: bool, value: *i32) ?*i32 {
    if (ok) return value;
    return null;
}
```

This returns a pointer to a number owned elsewhere.

Use an optional value when you want to return data directly.

Use an optional pointer when you want to refer to existing data.

#### Optional Pointer vs Empty Slice

Sometimes beginners use `null` when an empty slice would be better.

For sequences, prefer an empty slice when “no items” is a valid sequence.

```zig
[]const u8
```

can represent both:

```text
some bytes
zero bytes
```

An empty slice has length `0`.

```zig
const empty = bytes[0..0];
```

Use an optional slice only when there is a real difference between:

```text
no slice was provided
```

and:

```text
a slice was provided, but it is empty
```

The same idea applies to pointers.

Use `null` only when absence means something.

#### Optional Pointer Syntax Summary

| Syntax | Meaning |
|---|---|
| `*T` | pointer to mutable `T`, cannot be null |
| `*const T` | pointer to read-only `T`, cannot be null |
| `?*T` | pointer to mutable `T`, or null |
| `?*const T` | pointer to read-only `T`, or null |
| `null` | no value |
| `if (maybe) \|p\|` | unwrap optional pointer |

#### Common Mistake: Dereferencing Before Checking

This is wrong:

```zig
const maybe: ?*i32 = null;

// maybe.* = 10; // error
```

You must check first:

```zig
if (maybe) |p| {
    p.* = 10;
}
```

This is not just syntax. It is a safety rule.

The program must prove that the pointer exists before using it.

#### Common Mistake: Using Optional Pointers Too Often

Optional pointers are useful, but they should not be the default.

This is weak API design:

```zig
fn draw(image: ?*Image) void
```

If `draw` cannot do anything useful without an image, then the parameter should be:

```zig
fn draw(image: *Image) void
```

Now the caller must provide a real image.

Use optional pointers only when `null` is a real, expected state.

#### The Main Idea

A normal pointer must point to a valid value.

An optional pointer may point to a valid value, or it may be `null`.

This is the difference:

```zig
*T   // must exist
?*T  // may be absent
```

Before using an optional pointer, unwrap it with `if`, `orelse`, or another optional-handling form.

Optional pointers make absence explicit. They help you write APIs where null is visible in the type instead of hidden as a runtime surprise.

