Skip to content

Io.Reader of std.fs.File cannot be abstarcted #25968

@GPotoshin

Description

@GPotoshin

zig-0.16.0-67c9d57
I wrote code where an Io.Reader is chosen based on the code execution. This is a natural pattern.

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    const allocator = arena.allocator();
    defer arena.deinit();
    
    const lib: []const Package = @import("library.zon");

    const target = CmdLine.string("install") orelse {
        std.debug.print("not downloading\n", .{});
        return;
    };

    var reader: std.Io.Reader = undefined;

    if (CmdLine.hasFlag("-f")) {
        const file = try allocator.create(std.fs.File);
        file.* = try std.fs.cwd().openFile(target, .{});
        reader = std.fs.File.reader(file.*, try allocator.alloc(u8, 4096)).interface;
    } else {
        for (lib) |pkg| {
            std.debug.print("{s}", .{pkg.name});
            if (std.mem.eql(u8, pkg.name, target)) {
                const response = try allocator.create(std.ArrayListUnmanaged(u8));
                response.* = try get(allocator, pkg.sources.url);
                reader = std.Io.Reader.fixed(response.items);
            }
        }
    }

    var decompressor = try std.compress.xz.Decompress.init(&reader,
        allocator, try allocator.alloc(u8, 0));

    var file_buf: [4096]u8 = undefined;
    var file = try std.fs.cwd().createFile("secret.tar", .{.read = true});
    var writer = std.fs.File.writer(file, &file_buf).interface;
    _ = try decompressor.reader.stream(&writer, .unlimited);
    defer file.close();
}

The code compiles, given appropriate argument parsing. However, when I take the branch where the target is a file, the file reader fails to serve requests from the decompressor.

/usr/local/Paquets/zig-0.16.0-67c9d57/build/stage3/lib/zig/std/fs/File.zig:1294:25: 0x1021d6213 in readVec (gpq)
            .failure => return error.ReadFailed,
                        ^
/usr/local/Paquets/zig-0.16.0-67c9d57/build/stage3/lib/zig/std/Io/Reader.zig:1099:36: 0x102190343 in fillUnbuffered (gpq)
    while (r.end < r.seek + n) _ = try r.vtable.readVec(r, &bufs);
                                   ^
/usr/local/Paquets/zig-0.16.0-67c9d57/build/stage3/lib/zig/std/Io/Reader.zig:1085:5: 0x10216f957 in fill (gpq)
    return fillUnbuffered(r, n);
    ^
/usr/local/Paquets/zig-0.16.0-67c9d57/build/stage3/lib/zig/std/Io/Reader.zig:486:5: 0x1021639cf in peek (gpq)
    try r.fill(n);
    ^
/usr/local/Paquets/zig-0.16.0-67c9d57/build/stage3/lib/zig/std/Io/Reader.zig:534:20: 0x102142287 in take (gpq)
    const result = try r.peek(n);
                   ^
/usr/local/Paquets/zig-0.16.0-67c9d57/build/stage3/lib/zig/std/Io/Reader.zig:551:13: 0x1021be833 in takeArray__anon_20609 (gpq)
    return (try r.take(n))[0..n];
            ^
/usr/local/Paquets/zig-0.16.0-67c9d57/build/stage3/lib/zig/std/compress/xz/Decompress.zig:63:19: 0x1021be2df in init (gpq)
    const magic = try input.takeArray(6);
                  ^
/Users/giorno/projects/gpq/src/main.zig:92:24: 0x1021bf20b in main (gpq)
    var decompressor = try std.compress.xz.Decompress.init(&reader,
                       ^

But if I change the inner section to

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    const allocator = arena.allocator();
    defer arena.deinit();
    
    const lib: []const Package = @import("library.zon");

    const target = CmdLine.string("install") orelse {
        std.debug.print("not downloading\n", .{});
        return;
    };

    var file_reader: std.fs.File.Reader = undefined;
    var reader: std.Io.Reader = undefined;

    if (CmdLine.hasFlag("-f")) {
        const file = try allocator.create(std.fs.File);
        file.* = try std.fs.cwd().openFile(target, .{});
        file_reader = std.fs.File.reader(file.*, try allocator.alloc(u8, 4096));
    } else {
        for (lib) |pkg| {
            std.debug.print("{s}", .{pkg.name});
            if (std.mem.eql(u8, pkg.name, target)) {
                const response = try allocator.create(std.ArrayListUnmanaged(u8));
                response.* = try get(allocator, pkg.sources.url);
                reader = std.Io.Reader.fixed(response.items);
            }
        }
    }

    var decompressor = try std.compress.xz.Decompress.init(&file_reader.interface,
        allocator, try allocator.alloc(u8, 0));

    var file_buf: [4096]u8 = undefined;
    var file = try std.fs.cwd().createFile("secret.tar", .{.read = true});
    var writer = std.fs.File.writer(file, &file_buf).interface;
    _ = try decompressor.reader.stream(&writer, .unlimited);
    defer file.close();
}

the error is resolved. I found it was located in std/fs/File.zig:readVec, caused by the compiler's requirement for a traceable source (a file reader in this case) to perform a comptime upcast of the Io.Reader. I consider this an inconvenient constraint that isn't evident in the high-level user code. While I'm aware this is being addressed, I wanted to document this specific pattern, which I believe should be allowed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionNo questions on the issue tracker, please.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions