Non-obvious ways where Zig is just different.
lvalue semantics reach through control flow
const std = @import("std");
test "address of an optional's payload" {
var n: ?u8 = 1;
const p1: *u8 = &(n orelse return error.Unexpected);
p1.* = 3;
try std.testing.expectEqual(3, n.?);
// same thing
const p2 = if (n) |*p| p else return error.Unexpected;
try std.testing.expectEqual(p1, p2);
// p1 and p2 don't necessarily have the same address as &n .
// That depends on how optionals are laid out in memory.
var m: u8 = 2;
n = null;
const p3: *u8 = &(n orelse m);
p3.* += 2;
try std.testing.expectEqual(m, 4);
var o: ?u8 = 3;
const p4: *u8 = &(n orelse o.?);
p4.* += 2;
try std.testing.expectEqual(o, 5);
}
test "address of switch" {
var a: u8 = 0;
var b: u8 = 0;
var c: u8 = 0;
for (0..3) |i| {
const p = &switch (i) {
0 => a,
1 => b,
else => c,
};
p.* = @intCast(i + 1);
}
try std.testing.expectEqual(.{ 1, 2, 3 }, .{ a, b, c });
}
test "write to 'evil' after its lifetime" {
var p: *usize = undefined;
(blk: {
var evil: usize = undefined;
p = &evil;
break :blk evil;
}) = 5;
try std.testing.expectEqual(5, p.*);
}
zig has supports integers of unusual width
const std = @import("std");
test "random integer widths" {
const n1: u3 = 7;
const n2: u100 = @bitCast(@as(i100, -1));
var n3: u10 = 0;
n3 -%= 1;
const n4: i50 = std.math.maxInt(i33);
const n5: i50 = std.math.maxInt(u33);
const n6 = @as(u1000, std.math.maxInt(u500)) - n5;
const n7: u0 = std.math.maxInt(u0);
std.debug.print(
\\n1 {}: {x}
\\n2 {}: {x}
\\n3 {s}: {}
\\n4 {}: {x}
\\n5 {}: {x}
\\n6 {}: {}
\\n7 {}: {}
\\
, .{
@TypeOf(n1), n1,
@TypeOf(n2), n2,
"i10", @as(i10, @bitCast(n3)),
@TypeOf(n4), n4,
@TypeOf(n5), n5,
@TypeOf(n6), n6,
@TypeOf(n7), n7,
});
}