Zig: How to handle an optional error union type? (e.g., ?std.process.NextError![:0]u8)

Viewed 638

Context

Very new to the language, so bear with me. I'm writing a super basic function to print out the command line arguments passed to the program. Here is the critical logic:

    // already created allocator (std.heap.ArenaAllocator) and iterator (std.process.ArgIterator)

    var idx: u16 = 0;
    while (true) {
        var arg = iterator.next(&allocator.allocator) catch |err| {
            // ...
        };
        if (arg == null) {
            print("End of arguments, exiting.", .{});
            break;
        }
        print("Argument {d}: {s}", .{idx, arg});
        idx += 1;
    }

However, I'm getting an error message:

error: expected error union type, found '?std.process.NextError![:0]u8'
var arg = iterator.next(&allocator.allocator) catch |err| return err;

I think that the issue is tied to the fact that NextError returns an optional error union. I can't be sure though as I haven't found any documentation that covers this specific case.

Question

I got this code to work by removing the catch and pretending the error part of the return type doesn't exist. But the question is, what's the right way to catch that error?

2 Answers

You either need to use .? or put it in an else with a capture:

    if (arg == null) {
        print("End of arguments, exiting.", .{});
        break;
    } 
    print("Argument {d}: {s}", .{idx, arg.?});
    idx += 1;

    if (arg) |a| {
        print("Argument {d}: {s}", .{idx, a});
        idx += 1;
    } else {
        print("End of arguments, exiting.", .{});
        break;
    }

A bit late but might be useful still, In your code snippet you are reading args as a iterator. Using the iterator in a while with a error union works sometime otherwise unwrapping in the body.

// already created allocator (std.heap.ArenaAllocator) and iterator (std.process.ArgIterator)

var argIndex: usize = 0;
// If you still need the index in a while you can use a continuation.
while (iterator.next(arenaAllocator.allocator())) |maybeArg| : (argIndex += 1) {
        // let error surface elsewhere 
        // var arg = try maybeArg;
        // Or trap error here using catch:
        var arg = maybeArg catch |err| {
            std.debug.print("I just got here, I swear!\n{s}\n", .{ @errorName(err) });
            return err;
        };
        std.debug.print("Argument {d}: {s}\n", .{ argIndex, arg });
    } else {
            // The trailing else will only be used if a null is found after unwrapping 
            // the optional null or break is used. 
            // It is possible to capture errors from a while with a trailing else but not
            // if the entry capture is for a optional null.capture errors here.
        std.debug.print("All arguments printed.\n", .{});
    }

Another option for returning values from loops etc is using blocks, but probably overkill for this. Got most of this from reading std. The error/optional coercion tripped me up at the start.

Related