#Why does std.json.Parsed store a pointer to the allocator?

1 messages · Page 1 of 1 (latest)

keen arch
#

I started learning zig this week and reading through the standard library I stumbled upon the Parsed struct. I love how simple the pattern is, but I have been trying to understand why store a pointer to the allocator and not the arena allocator itself.

What's the reason behind creating the arena allocator in the heap using it's underlying allocator?

(from https://ziglang.org/documentation/0.12.0/std/#src/std/json/static.zig)

pub fn Parsed(comptime T: type) type {
    return struct {
        arena: *ArenaAllocator,
        value: T,

        pub fn deinit(self: @This()) void {
            const allocator = self.arena.child_allocator;
            self.arena.deinit();
            allocator.destroy(self.arena);
        }
    };
}

// ... and used ...

var parsed = Parsed(T){
    .arena = try allocator.create(ArenaAllocator),
    .value = undefined,
};
errdefer allocator.destroy(parsed.arena);
parsed.arena.* = ArenaAllocator.init(allocator);
errdefer parsed.arena.deinit();

Why not embed the arena allocator in Parsed and avoid an indirection?

pub fn Parsed(comptime T: type) type {
    return struct {
        arena: ArenaAllocator,
        value: T,

        pub fn deinit(self: @This()) void {
            self.arena.deinit();
        }
    };
}

Maybe I'm overthinking this, but I'm sure there is a thought process behind it that I'm missing.

marsh perch
#

storing the arena by pointer means that when you call allocator() on it, the mem.Allocator vtable pointers are correct. if it were by value and you returned the arena from a fn, those pointers would become invalidated.

sturdy isle
#

that doesn't really matter in this case though

#

tbh it's kind of weird that Parsed does this

marsh perch
#

sorry, not the vtable pointers, but the pointer to the parent allocator

sturdy isle
#

there's nothing really wrong with doing it this way, but it is kind of odd

marsh perch
#

no i think its correct. cause the arena gets constructed in one of the parse methods. so returning it would mean that if you created an ArrayList for instance with it, it's allocator field would point to invalidated memory.

sturdy isle
#

nah, parse just constructs an arena and immediately calls parseLeaky

#

So the arena is on the stack in the exact same place for the entire duration of parsing

marsh perch
#

well maybe it doesn't pertain here, but I've definitely seen people construct an arena in some fn and pass down arena.allocator(), then return the arena and end up with segfaults when trying to deinit.

haughty pilot
#

Possibly a holdover from the original API?

#

Though travis does bring up a fair point

#

Although, std.json should probably be using unmanaged data structures to elide that being a problem in the first place

keen arch
#

Ok, thanks for the replies. It makes a bit more sense. I guess that its done this way to avoid potential errors if something stores the allocator other than the Parsed struct. Because then the vtable pointers would be invalid after it leaves the scope.