When trying to unwrap an optional ArenaAllocator
inside a struct method, the program panics even though the same logic works fine outside of the method.
The issue seems related to self.arena
being an optional, but the behavior is inconsistent:
if (self.arena) |a| {}
inside a method panics- but
if (ms1.arena) |a| {}
outside works fine
Zig Version: 0.14.1
const std = @import("std");
const MyStrucut = struct {
l: ?[]u32,
arena: ?std.heap.ArenaAllocator,
allocator: *const std.mem.Allocator,
dv: ?u32, // dummy value
const this = @This();
pub fn Init() this {
const ms: MyStrucut = .{
.l = null,
.arena = null,
.allocator = undefined,
.dv = 56,
};
return ms;
}
pub fn Allocate(self: *this) !void {
self.arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
self.allocator = &self.arena.?.allocator();
}
pub fn StoreValue(self: *this, v: u32) !void {
self.l = try self.allocator.alloc(u32, 1);
self.l.?[0] = v;
}
pub fn PrintValue(self: *this) void {
std.debug.print("Stored value: {d}\n", .{self.l.?[0]});
}
pub fn deinit(self: *this) void {
self.arena.?.deinit();
}
pub fn WhyPanic(self: *this) void {
if (self.dv) |a| {
std.debug.print("{any}\n", .{a});
}
}
pub fn WhyPanic2(self: this) void {
if (self.arena) |a| {
_ = a;
}
}
};
pub fn main() !void {
var ms1 = MyStrucut.Init();
defer ms1.deinit();
try ms1.Allocate();
// These panic unexpectedly:
// ms1.WhyPanic();
// ms1.WhyPanic2();
// But this works fine:
if (ms1.arena) |a| {
_ = a;
}
try ms1.StoreValue(21);
ms1.PrintValue();
}
Expected Behavior
ms1.WhyPanic()
should print56
ms1.WhyPanic2()
should not panic, same as the outsideif (ms1.arena) |a| {}
block.
Actual Behavior
- Both
ms1.WhyPanic()
andms1.WhyPanic2()
panic at runtime. - Equivalent logic outside a struct method works as expected.
But when i remove arena from struct like this
const std = @import("std");
const MyStrucut = struct {
l: ?[]u32,
// arena: ?std.heap.ArenaAllocator,
allocator: *const std.mem.Allocator,
dv: ?u32, // dummy value
const this = @This();
pub fn Init() this {
const ms: MyStrucut = .{
.l = null,
// .arena = null,
.allocator = undefined,
.dv = 56,
};
return ms;
}
pub fn Allocate(self: *this) !void {
// self.arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
self.allocator = &std.heap.page_allocator;
}
pub fn StoreValue(self: *this, v: u32) !void {
self.l = try self.allocator.alloc(u32, 1);
self.l.?[0] = v;
}
pub fn PrintValue(self: *this) void {
std.debug.print("Stored value: {d}\n", .{self.l.?[0]});
}
pub fn deinit(self: *this) void {
self.allocator.free(self.l.?);
}
pub fn WhyPanic(self: *this) void {
if (self.dv) |a| {
std.debug.print("{any}\n", .{a});
}
}
pub fn WhyPanic2(self: this) void {
if (self.l) |a| {
_ = a;
}
}
};
pub fn main() !void {
var ms1 = MyStrucut.Init();
defer ms1.deinit();
try ms1.Allocate();
ms1.WhyPanic(); // now not painc
ms1.WhyPanic2(); // now not painc
try ms1.StoreValue(21);
ms1.PrintValue();
}
Its work fine.