Seg faults using dir.Walker through interface

fn init(allocator: std.mem.Allocator, path: []const u8) !Store.Iterator {
    var dir = try std.fs.openDirAbsolute(path, .{ .iterate = true });
    var iter = Iterator{
        .walker = try dir.walk(allocator),
        .dir = dir,
    };
    return Store.Iterator.init(&iter); // <- problem!
}

You are returning something that contains a reference to a variable on the init stack frame, which will no longer be valid after init returns. It might work the first iteration, but the location in memory it points to will be overwritten by garbage the next time the stack grows to that point.

Instead of having FileSystemStore.Iterator.init directly return a Store.Iterator, consider changing your design so it returns a FileSystemStore.Iterator, and then expose some kind of asStoreIterator method that takes a reference to FileSystemStore.Iterator (which will have a lifetime determined by the caller) and returns the type-erased Store.Iterator. This is similar to the pattern interfaces like Allocator and Reader/Writer use in the standard library:

// this is the impl-specific state, which lives on this stack frame
var gpa_state: std.heap.GeneralPurposeAllocator(.{}) = .init;
defer std.debug.assert(gpa_state.deinit() == .ok);

// this is the type-erased context pointer and function pointer(s)
const allocator = gpa_state.allocator();
2 Likes