Test allocator segfault with std.Io.Writer.Allocating

I’m likely doing something wrong, but I was trying to use std.Io.Writer.Allocating and it works in dev, but not in test. Here’s an example where I have a function that takes a writer and writes a bit of text. It works when running but not when testing:

// scratch.zig
const std = @import("std");
const t = std.testing;

pub fn writeContent(writer: *std.Io.Writer) !void {
    try writer.print("hello\n", .{});
}

pub fn main() !void {
    var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
    const allocator = debug_allocator.allocator();
    var alloc_buffer: std.Io.Writer.Allocating = .init(allocator);
    defer alloc_buffer.deinit();
    var writer = alloc_buffer.writer;

    try writeContent(&writer);
    try writer.flush();

    std.debug.print("{s}", .{writer.buffer});
}

test "writeContent prints into a buffer" {
    const allocator = t.allocator;
    var alloc_buffer: std.Io.Writer.Allocating = .init(allocator);
    defer alloc_buffer.deinit();
    var writer = alloc_buffer.writer;

    try writeContent(&writer);

    try t.expectEqualStrings("hello\n", writer.buffer);
}

:white_check_mark:

$ zig run scratch.zig
hello

:cross_mark:

$ zig test scratch.zig
Bus error at address 0x16afd6c45
???:?:?: 0x16afd6c45 in ??? (???)
/path/to/zig/0.16.0/lib/std/Io/Writer.zig:2624:42: 0x104f490af in ensureTotalCapacity (test)
        return ensureTotalCapacityPrecise(a, better_capacity);
                                         ^
/path/to/zig/0.16.0/lib/std/Io/Writer.zig:2617:35: 0x104f48fc3 in ensureUnusedCapacity (test)
        return ensureTotalCapacity(a, new_capacity);
                                  ^
/path/to/zig/0.16.0/lib/std/Io/Writer.zig:2705:35: 0x104f48957 in drain (test)
            a.ensureUnusedCapacity(bytes.len + splat_len + 1) catch return error.WriteFailed;
                                  ^
/path/to/zig/0.16.0/lib/std/Io/Writer.zig:539:26: 0x104e30f23 in write (test)
    return w.vtable.drain(w, &.{bytes}, 1);
                         ^
/path/to/zig/0.16.0/lib/std/Io/Writer.zig:551:51: 0x104ec1ca3 in writeAll (test)
    while (index < bytes.len) index += try w.write(bytes[index..]);
                                                  ^
/path/to/zig/0.16.0/lib/std/Io/Writer.zig:658:27: 0x104f4c05b in print__anon_36281 (test)
            try w.writeAll(literal);
                          ^
/path/to/scratch.zig:5:21: 0x104f4982f in writeContent (test)
    try writer.print("hello\n", .{});
                    ^
/path/to/scratch.zig:27:21: 0x104f49b33 in test.writeContent prints into a buffer (test)
    try writeContent(&writer);
                    ^
/path/to/zig/0.16.0/lib/compiler/test_runner.zig:291:25: 0x104f27d1f in mainTerminal (test)
        if (test_fn.func()) |_| {
                        ^
/path/to/zig/0.16.0/lib/compiler/test_runner.zig:73:28: 0x104f277fb in main (test)
        return mainTerminal(init);
                           ^
/path/to/zig/0.16.0/lib/std/start.zig:699:88: 0x104f255a3 in callMain (test)
    if (fn_info.params[0].type.? == std.process.Init.Minimal) return wrapMain(root.main(.{
                                                                                       ^
???:?:?: 0x181a6eb97 in start (/usr/lib/dyld)
error: the following test command terminated with signal ABRT:
.zig-cache/o/f554843f8ccc441ec28752ab86b2575e/test --seed=0x2e0fd3fd

I’m wondering if I’m just not using std.Io.Writer.Allocating correctly since it feels weird to access the raw buffer to read, but that’s the only way I could read out the written string. But the bigger issue is: why can I not even write using the test allocator?

Writer is an interface. It expects to be inside a larger object. When you copied it, you broke that assumption. You can only take pointers to it, or copy the whole object.

2 Likes

Ah, of course! I knew it needed to be a pointer, but I thought passing &writer would do that. I didn’t know that alloc_buffer.writer would copy. Thanks, that makes everything work great. Subtle!

2 Likes

If there is no & it will copy. (even then it is still a copy, just of the address instead)

Help with actual code belongs in the Help category. And you can mark a comment as a solution (might have to change categories first)

1 Like