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
}