Why is it ok to free `&.{}`

I can do following thing with no errors. Why??? It is not only compile time defined memory but also it is not allocated by gpa which doesn’t give me any errors even in debug :thinking:

const d: []u8 = &.{};
gpa.free(d);
2 Likes

When the memory to free has zero length, std.mem.Allocator.free is by its contract a no-op (i.e. it does nothing and just returns). It makes sense as there are literally zero bytes to free up.

Similarly, calling std.mem.Allocator.alloc with the n parameter set to 0 will return a dummy slice of length 0. It will never actually allocate anything. (see lines 295-298 here)

EDIT: This optimization also happens before any call to the allocator’s VTable, so you can be sure this holds true regardless of the particular allocator implementation.

14 Likes

To answer the question “why” the contract is this way:

It’s a good (DOD-style) design practice that degenerate cases should be no-ops instead of errors. Infallible is always better than fallible.

5 Likes

So basically speaking it should allow me to not use ArrayList or HashMap for very simple situations like this:

var required_features: []Feature = &.{};
defer gpa.free(required_features);

if (expected_usage.contains(.some_uasage)) {
    required_features = append(gpa, required_features, .{.feature1, .feature2 });
}

if (expected_usage.contains(.some_uasage2)) {
    required_features = append(gpa, required_features, .{.feature4, .feature5 });
}

It is probably very not intended usage but it is possible then

1 Like

This is necessary to allow code like

var some_list: std.ArrayList(u8) = getArrayList(gpa);
defer some_list.deinit(gpa);

functionThatTakesOwnership(some_list.toOwnedSlice(gpa));
6 Likes

ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ

1 Like

It does! A semi-special one. They take std.math.maxInt(usize) and round it down to the closest aligned address.

1 Like

Or if to cite zig zen:

Resource allocation may fail; resource deallocation must succeed.

4 Likes