Skip to main content

Payload Captures

Payload captures use the syntax |value| and appear in many places, some of which we've seen already. Wherever they appear, they are used to "capture" the value from something.

With if statements and optionals.

test "optional-if" {
var maybe_num: ?usize = 10;
if (maybe_num) |n| {
try expect(@TypeOf(n) == usize);
try expect(n == 10);
} else {
unreachable;
}
}

With if statements and error unions. The else with the error capture is required here.

test "error union if" {
var ent_num: error{UnknownEntity}!u32 = 5;
if (ent_num) |entity| {
try expect(@TypeOf(entity) == u32);
try expect(entity == 5);
} else |err| {
_ = err catch {};
unreachable;
}
}

With while loops and optionals. This may have an else block.

test "while optional" {
var i: ?u32 = 10;
while (i) |num| : (i.? -= 1) {
try expect(@TypeOf(num) == u32);
if (num == 1) {
i = null;
break;
}
}
try expect(i == null);
}

With while loops and error unions. The else with the error capture is required here.

var numbers_left2: u32 = undefined;

fn eventuallyErrorSequence() !u32 {
return if (numbers_left2 == 0) error.ReachedZero else blk: {
numbers_left2 -= 1;
break :blk numbers_left2;
};
}

test "while error union capture" {
var sum: u32 = 0;
numbers_left2 = 3;
while (eventuallyErrorSequence()) |value| {
sum += value;
} else |err| {
try expect(err == error.ReachedZero);
}
}

For loops.

test "for capture" {
const x = [_]i8{ 1, 5, 120, -5 };
for (x) |v| try expect(@TypeOf(v) == i8);
}

Switch cases on tagged unions.

const Info = union(enum) {
a: u32,
b: []const u8,
c,
d: u32,
};

test "switch capture" {
var b = Info{ .a = 10 };
const x = switch (b) {
.b => |str| blk: {
try expect(@TypeOf(str) == []const u8);
break :blk 1;
},
.c => 2,
//if these are of the same type, they
//may be inside the same capture group
.a, .d => |num| blk: {
try expect(@TypeOf(num) == u32);
break :blk num * 2;
},
};
try expect(x == 20);
}

As we saw in the Union and Optional sections above, values captured with the |val| syntax are immutable (similar to function arguments), but we can use pointer capture to modify the original values. This captures the values as pointers that are themselves still immutable, but because the value is now a pointer, we can modify the original value by dereferencing it:

test "for with pointer capture" {
var data = [_]u8{ 1, 2, 3 };
for (&data) |*byte| byte.* += 1;
try expect(eql(u8, &data, &[_]u8{ 2, 3, 4 }));
}