null is the value used when an optional has no payload.
const x: ?i32 = null;The type of x is not i32. It is ?i32.
This distinction is important. An integer and an optional integer are different types.
const a: i32 = 10;
const b: ?i32 = 10;
const c: ?i32 = null;a is always an integer.
b may hold an integer.
c holds no integer.
You cannot assign null to a non-optional type:
const x: i32 = null; // errorThere is no null integer, no null boolean, and no null struct. Only optional types can hold null.
This rule also applies to pointers.
var n: i32 = 123;
const p: *i32 = &n;
const q: ?*i32 = null;p must point to an i32.
q may point to an i32, or it may be null.
This removes a common source of C errors. In C, many pointer types may silently contain NULL. In Zig, nullability is written in the type.
A function can use null to say that no value was found:
fn indexOfByte(buf: []const u8, target: u8) ?usize {
for (buf, 0..) |b, i| {
if (b == target) {
return i;
}
}
return null;
}The return type is:
?usizeThis means the function returns either an index or null.
Use it like this:
const std = @import("std");
fn indexOfByte(buf: []const u8, target: u8) ?usize {
for (buf, 0..) |b, i| {
if (b == target) {
return i;
}
}
return null;
}
pub fn main() void {
const text = "hello";
const pos = indexOfByte(text, 'e');
std.debug.print("{any}\n", .{pos});
}The output is:
1Searching for a byte that does not occur returns null:
const pos = indexOfByte(text, 'z');The output is:
nullnull should not be used to hide errors. If a file cannot be opened, that is an error. If a name is not found in a table, that may be null.
Use null when absence is an ordinary result.
Use an error when the operation failed.
Exercise 9-5. Write a function lastIndexOfByte that returns the last position of a byte in a slice, or null.
Exercise 9-6. Declare a nullable pointer to an integer. Set it to null, then set it to the address of an integer.
Exercise 9-7. Why should a missing array element use null, while failure to read a file should use an error?