Returning pointer to stack allocated memory (returning a struct containing a slice)

Is this valid code? I suspect it is not, since returning the struct changes its location in memory (sometimes) and invalidates the slice?

Is this why std.BoundedArray exists?

pub const BadStruct = struct {
    data: [100]u8,
    slice: []u8,
}

fn make_bad_struct() BadStruct {
    const data = std.mem.zeros([100]u8);
    return BadStruct{
        .data = data,
        .slice = data[0..4],
    }
}

Your suspicions are correct. That code returns a dangling stack pointer. I found this article does a pretty good job of explaining whats happening.

If you wanted to check your assumptions, you might write a test like this which fails

const std = @import("std");
pub const BadStruct = struct {
    data: [100]u8,
    slice: []u8,
};

fn make_bad_struct() BadStruct {
    var data = std.mem.zeroes([100]u8);
    return .{
        .data = data,
        .slice = data[0..4],
    };
}

test {
    var s = make_bad_struct();
    try std.testing.expectEqual(&s.data, s.slice.ptr);
}

std.BoundedArray might be a good choice if slice will always start from 0. If not, you might just store the bounds and provide a slice() method. Maybe something like this:

pub const GoodStruct = struct {
    data: [100]u8,
    bounds: [2]u8,

    pub fn slice(x: *const GoodStruct) []const u8 {
        return x.data[x.bounds[0]..x.bounds[1]];
    }
};

fn make_good_struct() GoodStruct {
    return .{
        .data = std.mem.zeroes([100]u8),
        .bounds = .{ 0, 4 },
    };
}

test {
    const s = make_good_struct();
    try std.testing.expectEqual(&s.data, s.slice().ptr);
}

Its important to notice that slice() accepts a pointer. This way we don’t return a dangling pointer, but a reference to the data stored outside of the slice() method.

1 Like

We also have a doc here. Maybe @kj4tmp example worth adding into there.

1 Like