Suppose I have a struct like
const Fruit = struct {
name: []const u8,
};
This struct may be created programmatically, in which case the name
string will have to be free at some point -- so suppose we keep track of an allocator and have a deinit
method:
const Fruit = struct {
name: []const u8,
allocator: std.mem.Allocator,
pub fn deinit(self: *Fruit) void {
self.allocator.free(self.name);
self.* = undefined;
}
};
This works fine in most cases. However if at any point someone decides to hardcode an instance of this struct
var banana = Fruit{
.name = "banana",
.allocator = std.testing.allocator,
};
defer banana.deinit();
Then the call to allocator.free(self.name)
above will fail, because self.name
wasn't allocated by allocator
(I guess).
Is it possible to detect that inside deinit()
time so I don't try to free that string?
Or perhaps there is a magic way to turn the string literal into an allocated string?
What is the recommended approach here? (Please don't tell me to just not call deinit()
, assume there are other things that must be de-initialized and that is not an option.)
the "magic way" is just duping the string with an allocator when you assign it .name = alloc.dupe(u8, name)
, or just letting the user manage all of the strings and dont free them yourself.
you could also make your own data structure to carry that information on whether it was allocated or not:
const String = union(enum) {
static: []const u8,
alloced: []const u8,
};
For your case, I'd recommend doing something like this:
const Fruit = struct {
name: []const u8,
allocator: std.mem.Allocator,
pub fn init(allocator: std.mem.Allocator, name: []const u8) !Fruit {
return .{
.name = try allocator.dupe(u8, name),
.allocator = allocator,
};
}
pub fn deinit(self: *Fruit) void {
self.allocator.free(self.name);
self.* = undefined;
}
};
This way you can use Fruit
like this:
var b = try Fruit.init(allocator, "banana");
defer b.deinit();
And it will always be freed correctly