#Explaining &.{} Syntax

1 messages · Page 1 of 1 (latest)

dusky bramble
#

Hello,
I was trying to put an allocator in a struct, when I got some memory issues. Looking around, it seems that it boils down to the allocator function. I am still confused as to what &.{} does. I know we are taking a reference of a VTable, but where does the memory that vtable points to live? I am assuming that it doesn't live in the allocator function, so the function that calls allocator?

pub fn allocator(self: *FixedBufferAllocator) Allocator {
        return .{
            .ptr = self,
            .vtable = &.{
                .alloc = alloc,
                .resize = resize,
                .free = free,
            },
        };
    }
dawn coral
#

In this case, the outer .{} will be runtime-known because it depends on self, but the inner .{} is a constant because all the fields are comptime-known

#

In general, comptime-known expressions will become constants with static lifetimes. So things like fn f() *const i32 { return &5; } will work

#

The important thing to look out for is that the reference to the allocator instance is a pointer. So, the std.mem.Allocator instance should not be used after the FixedBufferAllocator goes out of scope

humble hawk
#

^^
if it weren't comptime-known -- e.g. if we replaced .alloc = alloc with .alloc = some_runtime_known_fn_ptr -- then the vtable would be a stack temporary local to the allocator function, so you would get a UAF

#

but as a general rule, if x is a comptime-known constant (meaning it is a comptime-known rvalue or const variable), &x is also a comptime-known constant which can be used globally, i.e. has infinite lifetime

dusky bramble
#

So if it's comptime know, the value will exist in global memory?

austere condor
#

Yeah, it would go in the rodata section usually

dusky bramble
#

Ok since vtable is stored in global memory, why does this not work?

const Resources = struct {

  buffer: [512]u8 = undefined,
  fba: std.heap.FixedBufferAllocator = undefined,
  arena: std.mem.Allocator = undefined,

  pub fn init() Resources {
        var self: Resources = Resources{};

        self.fba = std.heap.FixedBufferAllocator.init(&self.buffer);
        self.arena = self.fba.allocator();

        return self;
    }
};
#

The error I keep getting is telling me that I ran out of memory which idk why that is

dusky bramble
austere condor
#

not necessarily

#

only if the value they're set to is comptime known, which can be tricky

#

I'm not the best to explain it though, it depends on comptime propogation

dawn coral
dawn coral
#

You don't need to store std.mem.Allocator as a field. You can create it from an instance of Resource very cheaply by doing resource.fba.allocator()

austere condor
#

you can also make init take self and return nothing, as an alternative to returning a copy

dawn coral
#

You could, but what's the point? You'll have to make sure never to move it, and there's most likely zero gain of storing the std.mem.Allocator as a field

#

You're essentially just storing an extra pointer to an offset of the Resources instance inside itself

#

And an extra std.mem.Allocator.VTable pointer which will never change

dusky bramble
#

@dawn coral Oh I see it now in allocator(self: *FixedBufferAllocator)

austere condor
#

would Resources survive a copy without storing the arena? wouldn't the buffer be an issue?

dusky bramble
#

That makes sense

austere condor
#

well storing the arena wouldn't help clearly

dusky bramble
#

I create a local varaible, pass in a ref to allocator and then that does out of scope

#

Thanks y'all!

austere condor
#

yeah Resources still wouldn't survive being passed around by value, since that would copy the buffer if I understand correctly

dusky bramble
#

The reason why is I just need to manage some memory only in one function

#

I don't plan on returning it

dawn coral
#

You will need an init function in order to do what you're doing

#

Like @austere condor was saying

#

Because FixedBufferAllocator contains a pointer to the buffer

#

Currently you're doing FixedBufferAllocator.init(&self.buffer) but that self.buffer will go out of scope at the end of the function