n0s4
January 16, 2025, 11:36pm
1
Often, a call to Allocator.create
is succeeded by an immediate initialization of the returned memory. My idea is to have create
take an initialization value, and initialize the new memory itself. If you wanted to keep the prior behaviour, you would simply pass undefined
as the initialization value.
const std = @import("std");
const Foo = struct {
bar: i32 = 0,
baz: bool = false,
const init = Foo{ .bar = 10, .baz = true };
};
pub fn main() !void {
var arena = @import("std").heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
const before = try allocator.create(Foo, undefined);
const default = try allocator.create(Foo, .{});
const init = try allocator.create(Foo, .init);
const custom = try allocator.create(Foo, .{
.bar = -1,
.baz = false,
});
}
This has a two advantages:
It is explicit when memory is undefined. It’s easy for newcomers to not realize ‘create’ returns undefined memory, without reading the doc comment (hence ).
It is easier to read and write in the case where you were going to immediately initialize anyway.
Win Win! Unless I’m missing something?
1 Like
There is an open proposal suggesting exactly this:
opened 06:52PM - 19 Jul 24 UTC
breaking
standard library
proposal
This proposal competes with #15764.
In Zig, you typically have to explicitly … write `undefined` to get an undefined value. There are exceptions (e.g. `@intFromPtr(&{})` can give you `@as(usize, undefined)` without ever explicitly spelling that out), but as a general rule, it's considered bad form for a language feature or API to give you `undefined` values without you explicitly acknowledging that.
There is one obvious part of the standard library where this rule is not followed: allocators! Allocators always return uninitialized memory. This is familiar to those of us coming from C, but when you stop and think about it, is actually pretty difficult to justify. If I want a local variable to be uninitialized, I write `var x: u32 = undefined`, because this is far more clear; why do we not apply the same logic to `std.mem.Allocator.create`?
This would also make the most common usage pattern of `create` -- allocating a value, then immediately following it up with an assignment (`ptr.* = ...`) -- more convenient.
This proposal suggests to change the implementation of `std.mem.Allocator.create` to the following:
```zig
pub inline fn create(self: Allocator, comptime T: type, init_val: T) Error!*T {
if (@sizeOf(T) == 0) return comptime &init_val;
const ptr: *T = @ptrCast(try self.allocBytesWithAlignment(@alignOf(T), @sizeOf(T), @returnAddress()));
ptr.* = init_val;
return ptr;
}
```
The `inline` annotation is used here to allow propagation of a comptime-known `init_val`, which is particularly important in the case where it is `undefined`. An optimizer may not have good reason to inline this function, which would lead to an actual store of an `undefined` value to the allocated pointer, which could add up to a lot of wasted CPU cycles. In addition, the `inline` annotation probably helps to avoid binary bloat, at least in debug builds.
## Bonus Proposal
One can consider whether we should give the same treatment to `alloc`. A direct analogy would effectively transform it into `dupe`; however, this is unhelpful, as you typically don't have a slice to hand while you're trying to construct another slice! So, the only option seems to be to take an `init_val: T` and `@memset` the entire slice to this value. If we want to consider this, I propose it should apply to the following methods:
* `alloc`
* `allocWithOptions`
* `allocWithOptionsRetAddr`
* `allocSentinel`
* `alignedAlloc`
* `allocAdvancedWithRetAddr`
Going even deeper, we can ask whether `resize`/`realloc`/`reallocAdvanced` should take a parameter to init added buffer capacity with. However, my opinion is that this would be taking the idea to an unreasonable extreme.
5 Likes
n0s4
January 17, 2025, 12:23am
3
Whoops. That’s good to know, thanks.
Not pretty sure, but two “constructors” seem to be more or less close to the topic.