Skip to main content
  1. Posts/

Zig Optionals

definitepotato
Author
definitepotato
Code slinger, golang, ziglang, grokking life, tabletop games enthusiast, obsession with keebs and numbers.
Table of Contents

Optional types in zig are the only type that is allowed to be null. Optionals wrap a child type. To access the child value you must unwrap the optional. This prevents accidental null pointer dereferencing. Let’s declare and unwrap them.

Declaring Optionals
#

Use ?T syntax to declare an optional of type T. This can store either null or a value of type T.

const std = @import("std");
const print = std.debug.print;

pub fn main() void {
    const maybe_value: ?i32 = null;
    print("{any}\n", .{maybe_value}); // null
}

Unwrapping Optionals
#

Method 1: if expression
#

Unwrap an optional using an if expression with or without a capture.

const std = @import("std");
const print = std.debug.print;
const rand = std.crypto.random;

fn giftsRemaining() ?usize {
    const quantity = rand.intRangeAtMost(usize, 0, 1);
    if (quantity == 0) return null;
    return quantity;
}

pub fn main() void {
    const gifts_remaining = giftsRemaining();

    // The capture unwraps the optional and extracts the child value.
    if (gifts_remaining) |quantity| {
        print("There is {d} gift remaining.\n", .{quantity}); // There is 1 gift remaining.
    } else {
        print("No gifts.\n", .{});
    }
}

Method 2: while expression
#

Unwrap an optional using a while expression with or without a capture.

const std = @import("std");
const print = std.debug.print;

var health_remaining: usize = 100;

fn attack(dmg: usize) ?usize {
  if (health_remaining == 0) return null;
  health_remaining -= dmg;
  return health_remaining;
}

pub fn main() void {
  while (attack(25)) |health| {
    print("Health Remaining: {d}\n", .{health}); // 75, 50, 25, 0
  }
}

Method 3: orelse expression
#

The orelse expression will set a fallback value when the optional is null.

const std = @import("std");
const print = std.debug.print;

pub fn main() void {
    const dog: ?[]const u8 = null;
    const sound = dog orelse "bark";

    print("Dog says: {s}\n", .{sound}); // Dog says: bark
}

Method 4: .? shorthand
#

The .? operator asserts the optional is not null and extracts the child value. .? is shorthand for orelse unreachable.

const std = @import("std");
const print = std.debug.print;

pub fn main() void {
    const score: ?usize = 50;
    const new_score = score.? + 1;

    print("Score: {d}\n", .{new_score}); // Score: 51
}

Related