Anonymous struct literals are widely used throughout Zig stdlib, especially implementing vtable
in all allocators. Documentation, unfortunately, has a lot to be desired and this thread carries over and expands the conversation that started in another thread that became too long.
Some fundamental questions first:
- Where do
&.{...}
live? On stack, in heap, in data or text segment? - How long do they live? Local, static?
- If I instantiate the same anonymous struct literal twice, do I get two distinct objects with identical content (at two different addresses) or I will get a single unique object (de-dupped) with both instances pointing to the same address?
The following example shows that the story is complex and, IMHO, goes against Zig philosophy of “locality” – what you see on one screen is what you get. Meaning you do not need to chase where values of all the fields in the struct literal are coming from in order to see where it is allocated. Unfortunately, as it stands now one has to carefully chase them.
I am still learning Zig and have holes in understanding how it works and would like to hear from others on the subject.
//! example.zig
const std = @import("std");
const stdout = std.io.getStdOut().writer();
const S = struct {
a: *const isize,
b: *const isize,
};
// Memory for Anonymous Struct literals is allocated in relationship with memory of its fields.
// If all the fields are constants that live in data or text segment than the struct literal will live there
// and its multiple instantiations are de-duped so that taking address returns the same value.
// If at least one of fields is a local value (a `var`) then struct literal lives on stack
// and every incarnation of the same struct literal will have its own distinct address.
pub fn main() !void {
const cn: isize = 42; // c1 == c2
var vn: isize = 123; // v1 != v2
var c1 : *const S = &.{.a=&cn, .b=&cn}; _ = &c1;
var c2 : *const S = &.{.a=&cn, .b=&cn}; _ = &c2;
var v1 : *const S = &.{.a=&vn, .b=&vn}; _ = &v1;
var v2 : *const S = &.{.a=&vn, .b=&vn}; _ = &v2;
var cv1: *const S = &.{.a=&cn, .b=&vn}; _ = &cv1;
var cv2: *const S = &.{.a=&cn, .b=&vn}; _ = &cv2;
try stdout.print("c1 == c2 -> {}\n", .{c1==c2});
try stdout.print("v1 == v2 -> {}\n", .{v1==v2});
try stdout.print("cv1==cv2 -> {}\n", .{cv1==cv2});
try stdout.print("&cn = {*}\n", .{&cn});
try stdout.print("c1 = {*}\n", .{c1});
try stdout.print("c2 = {*}\n", .{c2});
try stdout.print("&vn = {*}\n", .{&vn});
try stdout.print("v1 = {*}\n", .{v1});
try stdout.print("v2 = {*}\n", .{v2});
try stdout.print("cv1= {*}\n", .{cv1});
try stdout.print("cv2= {*}\n", .{cv2});
}
Running it on MacOS-13.6.3 with 0.12.0-dev.1834+f36ac227b
results in
c1 == c2 -> true
v1 == v2 -> false
cv1==cv2 -> false
&cn = isize@1010b40f8
c1 = struct-lit-00.S@1010b40e8
c2 = struct-lit-00.S@1010b40e8
&vn = isize@16ee12998
v1 = struct-lit-00.S@16ee129b8
v2 = struct-lit-00.S@16ee129d0
cv1= struct-lit-00.S@16ee129e8
cv2= struct-lit-00.S@16ee12a00