I need a global variable that contains a simple (statically known size) tree of types. Here is how I originally did it:
const Type = enum {
Type,
Number,
Int,
Int32,
Int64,
Float,
// ...
};
const Node = struct {
self: Type,
next: []const Node,
};
var tree: Node = Node{ .self = Type.Type, .next = &[_]Node{
Node{ .self = Type.Int, .next = &[_]Node{
Node{ .self = Type.Int32, .next = undefined },
Node{ .self = Type.Int64, .next = undefined },
} },
Node{ .self = Type.Float, .next = undefined },
} };
pub fn main() !void {
std.log.debug("{any}", .{tree});
}
Program gives the expected result:
debug: test.Node{ .self = test.Type.Type, .next = { test.Node{ .self = test.Type.Int, .next = { ... } }, test.Node{ .self = test.Type.Float, .next = { ... } } } }
Later on, I didn’t like the fact that I have to put all these struct initializers at the same time I define the tree
. So I tried to do all the dirty stuff within the labeled block:
var tree: Node = blk: {
var root = Node{ .self = Type.Type, .next = &[0]Node{} };
// Integers
var int = Node{ .self = Type.Int, .next = undefined };
const int32 = Node{ .self = Type.Int32, .next = undefined };
const int64 = Node{ .self = Type.Int64, .next = undefined };
int.next = &[_]Node{ int32, int64 };
root.next = root.next ++ [1]Node{int};
// Floats
const float = Node{ .self = Type.Float, .next = undefined };
root.next = root.next ++ [1]Node{float};
// ...
// Other types
// ...
break :blk root;
};
Thankfully, it just worked as expected. Next, out of curiosity, I recalled that there is a comptime {}
block, which should do the same “trick” as the labeled block did:
var tree: Node = undefined;
comptime {
var root = Node{ .self = Type.Type, .next = &[0]Node{} };
// Integers
var int = Node{ .self = Type.Int, .next = undefined };
const int32 = Node{ .self = Type.Int32, .next = undefined };
const int64 = Node{ .self = Type.Int64, .next = undefined };
int.next = &[_]Node{ int32, int64 };
root.next = root.next ++ [1]Node{int};
// Floats
const float = Node{ .self = Type.Float, .next = undefined };
root.next = root.next ++ [1]Node{float};
// ...
// Other types
// ...
tree = root;
}
However, this time the program did not compile:
src/test.zig:48:10: error: unable to evaluate comptime expression
tree = root;
~~~~~^~~~~~
src/test.zig:48:5: note: operation is runtime due to this operand
tree = root;
^~~~
What’s the matter? It feels like I should be able somehow to init this tree
var during the compilation.
I remember people were trying to do something similar:
var memory: [128]u8 = undefined;
comptime {
// writing directly
_ = try std.fmt.bufPrintZ(&memory, "{s}", .{"Hello world!"});
// or using an allocator interface
var fba = std.heap.FixedBufferAllocator.init(&memory);
const alloc = fba.allocator();
_ = try std.fmt.allocPrintZ(alloc, "{s}", .{"Hello world!"});
// the former does the same thing as the latter anyway..
}
But this doesn’t compile either.