I'm trying a thing that iterates over the fields of a struct and does a different action according to the type of the field:
const std = @import("std");
const S = struct {
letter: u8,
number: u64,
};
pub fn main() void {
var result: i64 = 0;
var s = S{ .letter = 'a', .number = 77 };
inline for ([_][]const u8{ "letter", "number" }) |fname| {
var v = @field(s, fname);
switch (@TypeOf(v)) {
u8 => result += 1,
u64 => result += 2,
else => unreachable,
}
}
std.debug.print("result={}\n", .{result});
}
My question is: will the field differentiation logic (the switch) happen at comptime?, i.e. will the actual compile program be similar to
result += 1;
result += 2;
-- or will it be more like
const type1 = u8;
switch (type1) {
u8 => result += 1,
u64 => result += 2,
else => unreachable,
}
const type2 = u64;
switch (type2) {
u8 => result += 1,
u64 => result += 2,
else => unreachable,
}
I was assuming these switches were unnecessary since everything required to compute these things are known at compile time, but apparently the switch is being evaluated, because if I declare S.number to be u32, for example, the execution panics at the unreachable.
If that is the case, how can I rewrite this to make it more performant?
Yes, but not because of the
inline for, it's because the thing you're switching on iscomptimeonly, atype. That means the entire switch will be replaced by whichever branch was hit at comptime, theinline forjust lets you do this more than once.So if we deconstruct it step by step (as the compiler would do) it might look something like this:
Step 1:
Step 2:
Step 3:
You can see this when you inspect the assembly code generated, even in debug mode it's just equivalent to:
Also, recommendation for iterating over a type's fields is to use either
@typeInfo(T).Struct.fields, orstd.meta.fieldslike this: