Description
Zig Version
0.14.0
Steps to Reproduce and Observed Behavior
When you return a pointer to a local const-qualified struct, Zig does not allocate that binding on the stack by default. As a result, the pointer remains valid after the function returns—even though it wasn’t explicitly moved to the heap or marked comptime. This behavior feels surprising and can lead to subtle bugs when you expect a dangling pointer.
const std = @import("std");
const Foo = struct {
bar: i64,
data: [10]u8,
};
fn createFooVar() *Foo {
var foo = Foo{
.bar = 2,
.data = [_]u8{0,1,2,3,4,5,6,7,8,9},
};
return &foo;
}
fn createFooConst() *const Foo {
const foo = Foo{
.bar = 2,
.data = [_]u8{0,1,2,3,4,5,6,7,8,9},
};
return &foo;
}
pub fn main() !void {
const fooVar = createFooVar();
const fooConst = createFooConst();
std.log.info("fooVar (corrupted) = {any}", .{fooVar});
std.log.info("fooConst (still intact) = {any}", .{fooConst});
}
output
info: fooVar (corrupted) = conv.Foo{ .bar = 140734223737152, .data = { 185, 248, … } }
info: fooConst (still intact) = conv.Foo{ .bar = 2, .data = { 0, 1, … } }
Expected Behavior
Both createFooVar and createFooConst should yield corrupted data or compile-time errors when returning a pointer to a local binding.
Alternatively, only a value explicitly marked comptime should live beyond the function scope.
This discrepancy violates the expectation that plain const implies a runtime stack allocation. It would be helpful to clarify whether this is intentional or an oversight in the compiler’s allocation strategy.