I started using Zig less than a week ago and fell in love with it. Development experience in Zig so far touched many concepts and reasonings I already saw in C and Rust that is thrillling!
I’m using Zig version 0.16.0
What I would like to do is initialize a buffer of a fixed length with random values at compile time. The problem is at comptime I don’t have the init variable as such I must provide an Io implementation myself.
In an attempt to be more clear I will share the (non working) cod of what I’m trying to accomplish.
const BUFFER_SIZE = 19;
const BUFFER = comptime fill_buffer();
fn fill_buffer() [BUFFER_SIZE]u8 {
var buffer: [19]u8 = undefined;
// I dont't have init. init.io.randomSecure(&buffer);
return buffer;
}
I searched on Google, searched the source code of the zig’s std library (a quick global search on VSCode) and searched this forum still nothing reaped my attention.
Anybody with a solution and also Zig’s best practices to handle such situations and/or any link to share?
I think comptime aims to be deterministic, so generating random numbers at comptime seems to me like something that isn’t really intended (if it can be accomplished somehow).
You could add a build option that is @imported in the code, or generate a file/module and use @embedFile with that.
Can you describe your intention a bit more, should this value change on every build?
Ok thank you. Later this day I will take some time to tinker around with Zig’s build magic. I’ll post here my solution. I might opt out for a custom PRNG not so cryptographically strong but good enough for my use case. But you said
so using current time as random seed value is not so deterministic
Precisely, what I want is a unique value for each build.
Sorry if I posted any no sense but I haven’t taken my morning coffee yet
as @Sze explained, comptime is deterministic, this means no I/O can occur at comptime as it has no guarantee of being deterministic.
There is also the fact that Io at comptime would be quite the technical challenge! comptime is basically a zig interpreter, to do I/O zig would either have to add builtins for it, or support comptime assembly/extern calls.
The former is easiest, but limits to platforms zig supports, the latter is very flexible but requires something akin to JIT compilation.
FYI, rust, jai, and I think lisp and forth, do all support I/O at compile time, so it has precedence.
And to be precise, both objectives are non-objectives in zig!
Part of the appeal of comptime is that is limited, so to use zig I/O at comptime is just something that won’t happen, maybe Io.Writer and Io.Reader at comptime, but the rest is less probable.
I’m not really sure if this kind of behavior is encouraged, since it can lead to build results being unreproducible, and reproducible builds have always been a goal that many package management platforms strive for (even though it’s really hard).
There is a pretty nice workaround that you can do. You can have the build.zig generate a number randomly, and then expose that as an import or just generate the file with the data that you need.
I don’t have code for this specific use case - but in my project I had a problem where I wanted to be able to switch the main that I am running, and i did something like this:
//Support custom test file / test main
{
const maybeMainTarget = b.option([]const u8, "mainFile", "File whose main we can test");
const maybeTestTarget = b.option([]const u8, "testFile", "File we run tests from");
// Generate re-direct file
const redirectFilePath = "src/buildGen.zig";
const testMainFn: []const u8 =
if (maybeMainTarget) |mFile|
try std.fmt.allocPrint(b.allocator, "pub const redirectMain = @import(\"{s}\").main;", .{trimWhitespaceAndSrcPrefix(mFile)})
else
"pub const redirectMain = @compileError(\"redirectMain accessed with wrong compiler flag\");"; // no main when we don't redirect
const testImport: []const u8 = if (maybeTestTarget) |tFile| try std.fmt.allocPrint(b.allocator, "_ = @import(\"{s}\");", .{trimWhitespaceAndSrcPrefix(tFile)}) else "";
const file = try std.Io.Dir.cwd().createFile(io,redirectFilePath, .{ .truncate = true });
defer file.close(io);
const format =
\\/// Automatically generated by build.zig
\\
\\pub const testingOtherMain = {};
\\
\\{s}
\\
\\test "test" {{ {s} }}
;
var buff: [4096]u8 = undefined;
var writer = file.writer(io, &buff);
const w = &writer.interface;
try w.print( format, .{maybeMainTarget != null, testMainFn, testImport });
try w.flush();
}
It’s a point of friction that zig doesn’t allow you to do certain things with comptime but you can do them in build.zig.
I’d say it’s one of those things that for me until I knew that it bothered me because I was trying to force comptime to work for that. But now I go to build.zig immediately and It no longer bothers me. Downside is there’s more boilerplate than you would get with comptime. So yeah, if you do the thing in build.zig I reckon it’s gonna work.
Sorry reply is so long, I didn’t have enough time to write you a shorter reply.
PS If anyone knows how to make the code for writing the file more concise I am all ears I would love to have something smaller.