Search code examples
arraysheap-memoryzig

Why is my (heap allocated) buffer not being written to in zig


I am a beginner zig programmer coming from Rust & Golang. I've been searching online for a couple hours now and have only managed to find absurdly complex and inefficient solutions that do not fit my problem.

//Import standard library
const std = @import("std");

pub fn main() !void {
    //Create an allocator
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};

    //Open desired file
    var file = try std.fs.cwd().openFile("./state-of-union.txt", .{});
    defer file.close();

    //Regular buffer for demonstration purposes
    var b = std.mem.zeroes([100]u8);
    const x = try file.readAll(&b);

    std.debug.print("Test bytes read: {any}\n", .{x});

    //My *ACTUAL* buffer where the error arises
    var buffer = std.ArrayList(u8).init(gpa.allocator());
    defer buffer.deinit();
    const temp = try file.readAll(buffer.items);

    //Added to show that my code is not reading into my heap allocated array
    std.debug.print("Actual bytes read: {any}\n", .{temp});

    //Print file contents as string
    std.debug.print("{s}\n", .{buffer.items});
}

Output:

~\Desktop\ztest via ↯ v0.12.0-dev.3152+90c1a2c41 
❯ zig build run
Test bytes read: 100
Actual bytes read: 0

Now, if I attempt to change buffer.items to &buffer.items like I did with the stack allocated buffer, I get a compile time error:

~\Desktop\ztest via ↯ v0.12.0-dev.3152+90c1a2c41 
❯ zig build run
run
└─ run ztest
   └─ zig build-exe ztest Debug native 1 errors
src\main.zig:21:35: error: expected type '[]u8', found '*[]u8'
    const temp = try file.readAll(&buffer.items);
                                  ^~~~~~~~~~~~~
C:\Program Files\zig\lib\std\fs\File.zig:1049:36: note: parameter type declared here
pub fn readAll(self: File, buffer: []u8) ReadError!usize {
                                   ^~~~
referenced by:
    callMain: C:\Program Files\zig\lib\std\start.zig:511:32
    WinStartup: C:\Program Files\zig\lib\std\start.zig:350:45
    remaining reference traces hidden; use '-freference-trace' to see all reference traces
error: the following command failed with 1 compilation errors:
C:\Program Files\zig\zig.exe build-exe -ODebug -Mroot=C:\Users\user\Desktop\ztest\src\main.zig --cache-dir C:\Users\user\Desktop\ztest\zig-cache --global-cache-dir C:\Users\user\AppData\Lo
cal\zig --name ztest --listen=-
Build Summary: 2/7 steps succeeded; 1 failed (disable with --summary none)
run transitive failure
└─ run ztest transitive failure
   ├─ zig build-exe ztest Debug native 1 errors
   └─ install transitive failure
      └─ install ztest transitive failure
         └─ zig build-exe ztest Debug native (reused)
error: the following build command failed with exit code 1:
C:\Users\user\Desktop\ztest\zig-cache\o\b91671406079ab60e0e46cf8f3eb5e2c\build.exe C:\Program Files\zig\zig.exe C:\Users\user\Desktop\ztest C:\Users\logan\Desktop\ztest\zig-cache C:\Users\user\AppData\Local\zig --seed 0xdd4267fc -Z3028407686deb58d run

The main function opens the desired file, then (after demonstration) declares a heap allocated buffer and attempts to read from the file and to the buffer. Then print the buffer contents.

Any help is appreciated, I truly have no clue what is wrong. (Also if anybody can link me to some official zig examples that would be truly incredible. I am having a hard time finding them.)


Solution

  • File.readAll tries to fill the provided slice up to its .len. But since you pass it the underlying slice of a freshly initialized and empty ArrayList, no memory has been allocated yet and the slice has .len equal to 0.

    Instead of creating an empty ArrayList, you could create one with capacity 100 by using std.ArrayList(u8).initCapacity(gpa.allocator(), 100) instead of .init(gpa.allocator()). Then I think you also need to call buffer.expandToCapacity() to set the underlying buffer.items.len to 100 as well, which will then be picked up by file.readAll.

    Or, simpler, keep your original code using init, but add buffer.resize(100) before file.readAll(buffer.items).

    (The ArrayList documentation seems to be broken/nonexistent at the moment, so you have to figure this out by reading the source code: ziglang.org/GitHub.)

    In addition, readAll won't read any more bytes if the end of the file has been reached, so if you call it twice in your example code and the file is smaller than 200 bytes, you have to keep that in mind.