A zig library that provides a type to mark resources, a method to clean resources, and a resource tracker to catch any leaks.
R(T) is a type that wraps a resource a T. For example, R([]u8) warps a slice of u8. To initialize it, call R([]u8).init. For example:
var r = R([]u8).init(try allocator.alloc(u8, 100), allocator, Allocator.mem.free);R(T).init has the following signature:
//function signature of init
pub fn init(value: T, resource_manager: anytype, comptime deinit_fn: anytype) @This() {...}For type T, R(T).init will take:
value: Tis the resource created by theresource_managerresource_manageris a pointer to the object used to create the resourcedeinit_fnis a function that takesresource_managerandTas it's arguments to free the resource
R(T) is basically a super fat pointer, so it recommend to not pass it around to other structs. Instead, use R(T).get to get the wrapped resource and pass that around.
var r_array: []u8 = r.get();Finally, you can call R(T).deinit manually to free the resource.
r.deinit(); //calls allocator.free(value) internallyThis library provides the function clean. This function iterates over a passed value's fields and calls any deinit it finds.
example:
const Foo = struct {
data: R([]u8),
single: R(*u64),
pub fn init(allocator: *std.mem.Allocator) !@This() {
var data = R([]u8).init(try allocator.alloc(u8, 10), allocator, std.mem.Allocator.free);
errdefer clean(data);
var data_v = data.get();
@memset(data_v.ptr, 0, data_v.len);
var single = R(*u64).init(try allocator.create(u64), allocator, std.mem.Allocator.destroy);
errdefer clean(single);
return @This(){
.data = data,
.single = single,
};
}
};
//clean only works on tagged unions. for untagged unions, the struct should implement a deinit function
const FooU = union(enum) {
foo: Foo,
};
const Bar = struct {
foo: [2]?FooU = undefined, //an array of optional tagged unions, will get cleaned by clean
array: std.ArrayList(u8), //has a deinit function that takes no parameters, will be called by clean
pub fn init(allocator: *std.mem.Allocator) !@This() {
return @This(){
.foo = .{ .{ .foo = try Foo.init(allocator) }, .{ .foo = try Foo.init(allocator) } },
.array = std.ArrayList(u8).init(allocator),
};
}
};
test "R" {
var resource_tracker = ResourceTracker.init(std.testing.allocator);
ResourceTracker.set(&resource_tracker);
defer clean(resource_tracker);
{
var foo = try Foo.init(std.testing.allocator);
defer clean(foo);
var bar = try Bar.init(std.testing.allocator);
defer clean(bar);
try bar.array.append('a');
}
}ResourceTraker is a type that will track R resources. To track R resources allocations, put the following at the start of your program:
var resource_tracker = ResourceTracker.init(std.testing.allocator);
ResourceTracker.set(&resource_tracker);
defer clean(resource_tracker);