Hey I am pretty new to zig and come from a C background. When I am working with zig and creating struct I came across the “pattern” where the struct has an init method that returns a struct. Something like that:
const Bra = struct {
...
pub fn init( ... ) Bra {
...
return Bra { ... };
}
}
pub fn main() void {
var b: Bra = Bra.init(...);
...
}
Coming from C, it seems pretty wasteful to copy data from one place to another. I am assuming that zig handles unneeded copies under the hood, but I am not quite sure if this is true (couldnt find anything about it). I am also trying to use godbolt, but I never really got anything usable compiled (in the end I used the compiler option -femit-asm=main.asm, which did the job in the end, but still couldnt really figure it out for sure ).
And when I want to allocaed Bra on the head I just allocaed some memory to the heap and init it with ‘some_heap_var.* = Bra.init(…)’ and at the end I dont really do much unnessary coping (because the compile understands that, can I be sure for that)?
Generally the optimizer seems to take care of avoiding the copy. However I have found a few edge cases where Zig does seem to make unnecessary copies:
For example in debug mode it seems that Zig makes extra copies if the variable is stored as var
: why is the machine code different for these two different function calls? · Issue #16368 · ziglang/zig · GitHub (note that the llvm takes care of this in the release)
And when a function isn’t inlined (most of the time these small init functions are inlined), Zig has sometimes trouble with optimizing: Missed optimization for functions returning a normal struct. · Issue #14358 · ziglang/zig · GitHub
1 Like
Why do we not pass a pointer to the location we want the struct to be init at? This currently seems like we need to hope that the compiler sees that this is an unnecessary copy.
What is the benefit of doing this:
const Bra = struct {
...
pub fn init( ... ) Bra {
...
return Bra { ... };
}
}
over this:
const Bra = struct {
...
pub fn init( b: *Bra ) void {
...
b.* = Bra { ... };
}
}
It seems that the first option is being promoted by Zig himself.
Well, llvm is competent enough to optimize this in most use cases, so I guess the standard library authors just favor simplicity over worrying about rare potential performance problems that don’t happen most of the time. And the init function with return type
var b = Bra.init();
is just simpler to use than
var b: Bra = undefined;
b.init();
3 Likes