I'm using zig 0.7.0.
and I'm trying to import a list of zig source files from an array. Each source file has a main
function (whose return type is !void
) that I would like to call. The array module_names
is known at compile time.
Here is what I tried to do:
const std = @import("std");
const log = std.log;
const module_names = [_][]const u8{
"01.zig", "02.zig", "03.zig", "04.zig", "05.zig",
};
pub fn main() void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
for (module_names) |module_name, i| {
const module = @import(module_name); // this fails
log.info("i {}", .{i});
try module.main();
}
}
Even if the array is known at compile time, @import(module_name)
gives me this error:
./src/main.zig:13:32: error: unable to evaluate constant expression
const module = @import(module_name);
^
./src/main.zig:13:24: note: referenced here
const module = @import(module_name);
I could understand the error if the array would be dynamically generated and only known at runtime, but here the module_names
array is known at compile time. So I am a bit confused...
Alternatively, I also tried to wrap the entire main
body in a comptime
block:
pub fn main() void {
comptime {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
for (module_names) |module_name, i| {
const module = @import(module_name); // no errors here
log.info("i {}", .{i});
try module.main();
}
}
}
Here @import(module_name)
gives me no errors, but the log.info
fails with this other error:
/home/jack/.zig/lib/zig/std/mutex.zig:59:87: error: unable to evaluate constant expression
if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null)
^
/home/jack/.zig/lib/zig/std/mutex.zig:65:35: note: called from here
return self.tryAcquire() orelse {
^
/home/jack/.zig/lib/zig/std/log.zig:145:60: note: called from here
const held = std.debug.getStderrMutex().acquire();
^
/home/jack/.zig/lib/zig/std/log.zig:222:16: note: called from here
log(.info, scope, format, args);
^
./src/main.zig:26:21: note: called from here
log.info("i {}", .{i});
Is this kind of dynamic import possible in zig?
I think the kind of import you're after is possible, my understanding is that @import
is taking a zig
source file and turning it into a struct type. It actually seems like something that could even be done at runtime (although not using @import
, which wants a comptime
parameter (this is the important part for your problem).
The reason why your first example fails is that the argument you're passing, module_name
isn't known at comptime
, your for
loop won't execute until your program runs.
Your instinct to solve the problem is correct, get the loop to evaluate at compile time (specifically the capture value and iterator); I think there are two things you could do fix it.
Wrapping the loop in a comptime
block as you have done will work, but you'll need to think about what exactly it means to evaluate expressions at compile time, and if it makes sense. I think the implementation of log
will prevent you from logging at compile time, so you'd need to collect the values you're interested in inside the loop, and log them once outside the comptime
block.
The other way you could fix it is to force the capture values of the loop to be evaluated at compile time by unrolling the loop using an inline for:
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
inline for (module_names) |module_name, i| {
const module = @import(module_name);
log.info("i {}", .{i});
try module.main();
}
}
Disclaimer: There might be a better way of doing this, I'm comparatively new to the language =D