Zig May Pass Anything By Reference
Zig may pass non-primitive parameters into function by reference. It may also pass non-primitive result location by reference.
To run the code snippets below, past the code into a file and run it with zig run file.zig
(only guaranteed to work with Zig 0.11.0).
Alternatively, you can also run the code on this Zig Playground. Thanks dpc_pw!
The code snippets are provided by Lemon Drop
and Levy
in the Zig Matrix room. (feel free to join!)
Here is a buggy program that you can run with Zig 0.11.0. The output will be unexpected!
const AAAA = struct {
foo: [100]u32,
};
fn aaaaa(a: AAAA, b: *AAAA) void {
b.*.foo[0] = 5;
std.debug.print("wtf: {}", .{ a.foo[0] });
}
pub fn main() !void {
var f: AAAA = undefined;
f.foo[0] = 0;
aaaaa(f, &f);
}
Lesson: do not alias, ever.
Here is another buggy program. x
is a result location, and is taken by reference on both sides of the assignment.
const std = @import("std");
const What = struct { a: u8, b: u8 };
pub fn main() void {
var x: What = .{ .a = 1, .b = 2 };
x = .{ .a = x.b, .b = x.a };
std.log.info("wtf: {}", .{x});
}
Lesson: do not put the same variable on both sides of an assignment.
Solution: Use a temp variable to store the new value, like how you swap two variables in C.
pub fn main() void {
var x: What = .{ .a = 1, .b = 2 };
const tmp = .{ .a = x.b, .b = x.a };
x = tmp;
std.log.info("wtf: {}", .{x});
}
Thoughts
I have long thought Zig works like C: when I pass a struct itself (not a pointer), it is passed by-copy. I am lucky to have never met a bug because of this misunderstanding.
oof
Further Readings
https://github.com/ziglang/zig/issues/12064
andrewrk has recommended watching this video on the topic of result location semantics.