Search code examples
zig

process.Child.spawn() fails to compile with "error: expected tuple or struct argument, found []u8"


I have written a basic command calling wrapper function which on the surface seems to check out syntactically. It is based on an example in the Zig Cookbook, which indeed does compile and run fine.

However, compiling fails with an error which I cannot get past, seemingly (but probably not truly) deep within the standard library.

Code:

const std = @import("std");
const Child = std.process.Child;

const RunError = error {
    Failed,
};

pub fn callCommand(alloc:std.mem.Allocator, command:[]const[]const u8) !std.ArrayList(u8) {
    var caller = Child.init(command, alloc);
    caller.stdout_behavior = .Pipe;
    caller.stderr_behavior = .Pipe;

    var stdout = std.ArrayList(u8).init(alloc);
    var stderr = std.ArrayList(u8).init(alloc);
    errdefer stdout.deinit();
    defer stderr.deinit();

    try caller.spawn(); // Error points to here...
    try caller.collectOutput(&stdout, &stderr, 1024);

    const res = try caller.wait();

    if(res.Exited > 0) {
        std.debug.print("{s}\n", stderr.items); // EDIT - this is the culprit, however
        return RunError.Failed;
    } else {
        return stdout;
    }
}

test {
    const alloc = std.testing.allocator;
    const out = try callCommand(alloc, &[_][]const u8{"ls", "-l"});
    defer out.deinit();
    std.debug.print("{s}\n", .{out.items});
}

Upon trying to compile:

$ zig test -freference-trace src/commands.zig
/home/tk-noodle/.local/var/zig/zig-linux-x86_64-0.13.0/lib/std/fmt.zig:88:9: error: expected tuple or struct argument, found []u8
        @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType));
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
referenced by:
    print__anon_5241: /home/tk-noodle/.local/var/zig/zig-linux-x86_64-0.13.0/lib/std/io/Writer.zig:24:26
    print: /home/tk-noodle/.local/var/zig/zig-linux-x86_64-0.13.0/lib/std/io.zig:324:47
    print__anon_6636: /home/tk-noodle/.local/var/zig/zig-linux-x86_64-0.13.0/lib/std/debug.zig:97:21
    unexpectedErrno: /home/tk-noodle/.local/var/zig/zig-linux-x86_64-0.13.0/lib/std/posix.zig:7319:24
    setreuid: /home/tk-noodle/.local/var/zig/zig-linux-x86_64-0.13.0/lib/std/posix.zig:3372:30
    spawnPosix: /home/tk-noodle/.local/var/zig/zig-linux-x86_64-0.13.0/lib/std/process/Child.zig:673:18
    spawn: /home/tk-noodle/.local/var/zig/zig-linux-x86_64-0.13.0/lib/std/process/Child.zig:242:20
    callCommand: src/commands.zig:18:15
    test_0: src/commands.zig:35:21

In particular, it seems that when compiling it gets to lib/std/posix.zig:3372:30 , which passes down an error enum to lib/std/posix.zig:7319:24

Through some mangling I did confirm that .{@intFromEnum(err)} resolves to a struct{u16}, but somehow this is passed down eventually leading to the error stating that it sees a []u8.

It looks on the surface like a compiler issue, but if I change my code to return !void instead and simply discard the stdout/stderr , the program happily compiles, and test passes.

So my question is twofold:

  • what have I done wrong to cause this error ?
  • why does the error look like it is pointing to issues in the stdlib, instead of my own code ?

Solution

  • In your code:

    std.debug.print("{s}\n", stderr.items);
    

    This line is supposed to be:

    std.debug.print("{s}\n", .{ stderr.items });