# Optional Types

### Optional Types

An optional type is a type that can hold either a value or no value.

In Zig, “no value” is written as `null`.

For example:

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

The type `?i32` means:

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

So this variable can hold an integer:

```zig
const a: ?i32 = 123;
```

Or it can hold no integer:

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

The `?` is part of the type. It says: this value may be missing.

#### Why Optional Types Exist

Many programs need to represent “maybe there is a value.”

For example:

A search function may find an item, or it may find nothing.

A parser may read a number, or the input may not contain a number.

A pointer may point to something, or it may point to nothing.

A configuration field may be provided, or it may be absent.

Without optional types, programmers often use special values:

```zig
// Bad style for this case
// -1 means "not found"
const index: i32 = -1;
```

This works, but it is fragile. The meaning of `-1` is only a convention. The type system does not know that `-1` means “missing.”

With an optional type, the meaning is explicit:

```zig
const index: ?usize = null;
```

Now the type itself says that the value may be absent.

#### The Shape of an Optional Type

An optional type is written like this:

```zig
?T
```

where `T` is the real type inside.

Examples:

```zig
?i32
?usize
?bool
?[]const u8
?*User
```

Read them like this:

```text
?i32         maybe an i32
?usize       maybe a usize
?bool        maybe a bool
?[]const u8  maybe a string slice
?*User       maybe a pointer to User
```

The optional type wraps another type.

So this:

```zig
const name: ?[]const u8 = "Zig";
```

means:

```text
name is either a string slice, or null
```

#### A Simple Example

Suppose we want a function that finds the first even number in a list.

Sometimes the list has an even number. Sometimes it does not.

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

fn firstEven(numbers: []const i32) ?i32 {
    for (numbers) |n| {
        if (@mod(n, 2) == 0) {
            return n;
        }
    }

    return null;
}

pub fn main() void {
    const values = [_]i32{ 3, 7, 9, 12, 15 };

    const result = firstEven(values[0..]);

    if (result) |value| {
        std.debug.print("first even number: {}\n", .{value});
    } else {
        std.debug.print("no even number found\n", .{});
    }
}
```

The function returns `?i32`.

```zig
fn firstEven(numbers: []const i32) ?i32
```

That return type says:

```text
This function may return an i32.
It may also return null.
```

Inside the function, we return a normal integer when we find one:

```zig
return n;
```

If we do not find one, we return `null`:

```zig
return null;
```

#### Checking an Optional Value

You cannot use an optional value exactly like the value inside it.

This is not allowed:

```zig
const maybe_number: ?i32 = 10;
const doubled = maybe_number * 2; // error
```

Why? Because `maybe_number` might be `null`.

Before using the inner `i32`, you must unwrap the optional.

The most common way is `if`:

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

if (maybe_number) |number| {
    const doubled = number * 2;
    _ = doubled;
}
```

Inside this block, `number` is not optional. It is a plain `i32`.

This syntax:

```zig
if (maybe_number) |number| {
    // use number here
}
```

means:

```text
If maybe_number contains a value, call that value number and run this block.
```

You can also use `else`:

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

if (maybe_number) |number| {
    std.debug.print("number: {}\n", .{number});
} else {
    std.debug.print("no number\n", .{});
}
```

#### Optional Values Make Absence Explicit

Optional types are useful because they force you to handle the missing case.

Consider this function:

```zig
fn findUser(id: u64) ?User {
    // returns a User if found
    // returns null if not found
}
```

The caller cannot pretend that the user always exists. The return type says otherwise.

The caller must write code like this:

```zig
if (findUser(42)) |user| {
    // use user
} else {
    // handle missing user
}
```

This makes the program clearer.

A function that returns `User` promises that it always returns a user.

A function that returns `?User` says that the user may be missing.

Those are different promises.

#### Optional Is Not an Error

An optional value means “there may be no value.”

It does not mean “something went wrong.”

For example, searching a list and finding nothing is usually not an error. It is a normal result.

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

    return null;
}
```

If the target is absent, returning `null` is reasonable.

But if a file cannot be opened, that is different. There may be many reasons: permission denied, file not found, disk failure, invalid path. That should usually be an error union, not a plain optional.

```zig
fn openFile(path: []const u8) !File {
    // may fail with an error
}
```

Use an optional when absence is an ordinary possibility.

Use an error when something failed and the caller may need to know why.

#### Optional Pointers

Optional pointers are common in Zig.

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

This means:

```text
maybe a pointer to i32, or null
```

This is different from a normal pointer:

```zig
const ptr: *i32 = undefined;
```

A normal `*i32` cannot be `null`. If a pointer may be missing, write it as optional:

```zig
?*i32
```

This is an important Zig rule. Nullability is explicit.

In C, many pointer types can be null. In Zig, a pointer is non-null unless the type says otherwise.

That makes pointer code easier to reason about.

#### Optional Fields in Structs

Optional types are often used in structs.

```zig
const User = struct {
    id: u64,
    name: []const u8,
    email: ?[]const u8,
};
```

Here, every user has an `id` and a `name`.

But `email` is optional.

```zig
const alice = User{
    .id = 1,
    .name = "Alice",
    .email = "alice@example.com",
};

const bob = User{
    .id = 2,
    .name = "Bob",
    .email = null,
};
```

This is clearer than using an empty string to mean “no email.”

```zig
.email = ""
```

An empty string is still a string. It may mean “the email is empty.” It may mean “unknown.” It may mean “not provided.” The type does not say.

With `?[]const u8`, the meaning is explicit.

#### Optional Values and Defaults

Sometimes you want to use a default value when an optional is null.

Zig provides the `orelse` operator:

```zig
const maybe_port: ?u16 = null;
const port = maybe_port orelse 8080;
```

This means:

```text
If maybe_port has a value, use it.
Otherwise, use 8080.
```

Another example:

```zig
const maybe_name: ?[]const u8 = null;
const name = maybe_name orelse "guest";
```

Now `name` is a normal `[]const u8`, not an optional.

#### Optional Types Are Small but Important

Optional types look simple, but they change how you design APIs.

Compare these two functions:

```zig
fn getName() []const u8
```

and:

```zig
fn getName() ?[]const u8
```

The first function promises that a name always exists.

The second function says that a name may be absent.

That small `?` changes the contract.

Good Zig code uses optional types when absence is part of the normal model. This keeps the program honest. The type tells the reader what can happen, and the compiler makes sure the code handles it.

