Pointers are often optional.
A search may not find an object. A tree node may not have a parent. A linked list node may not have a next node.
In Zig, this is written explicitly:
?*i32This means:
either a pointer to
i32, ornull
A normal pointer:
*i32cannot be null.
This difference matters.
Here is a simple example:
const std = @import("std");
pub fn main() void {
var number: i32 = 123;
const p: ?*i32 = &number;
if (p) |ptr| {
std.debug.print("{d}\n", .{ptr.*});
}
}The output is:
123The expression:
ptr.*dereferences the pointer.
The payload capture:
if (p) |ptr|unwraps the optional pointer before use.
If the pointer is null:
const p: ?*i32 = null;the block is skipped.
Optional pointers are common in data structures.
A linked list node might look like this:
const Node = struct {
value: i32,
next: ?*Node,
};next may point to another node, or it may be null.
The last node in the list has:
next = nullThis style is direct and compact. There is no hidden nullable state.
Here is a small traversal example:
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("{d}\n", .{node.value});
current = node.next;
}
}The output is:
10
20
30Notice that current is optional:
var current: ?*Node = &first;The loop ends when current becomes null.
Optional pointers are safer than nullable C pointers because the type system forces the possibility of null to be handled.
In C, this is legal:
int *p = NULL;
printf("%d\n", *p);The program has undefined behavior.
In Zig, a plain pointer cannot hold null.
You must write:
?*i32and unwrap it before dereferencing.
Optional pointers can also be combined with slices:
?[]u8This means:
either a slice of bytes, or
null
This is useful when a buffer may or may not exist.
Exercise 9-11. Write a function that returns a pointer to the largest element in an array.
Exercise 9-12. Modify the linked list example so it computes the sum of all node values.
Exercise 9-13. Why is ?*T safer than allowing *T to contain null silently?