How to guard clause optionals (how to test for non-null)?

How do I do a guard clause on an optional? (Return early from a function at the top of the function for better readability)

pub fn foo(maybe_num: ?u8) void {
    if (!maybe_num) {
        return;
    } else |num| {
        // some incredibly complex and long task
        _ = num;
        return;
    }
}

test foo {
    foo(null);
}

The code above does not compile because the ! operator expects a bool

error: expected type 'bool', found '?u8'
    if (!maybe_num) {
         ^~~~~~~~~

I tried == null and now it wants an error union…

error: expected error union type, found 'bool'
    if (maybe_num == null) {

I guess zig wants this:

pub fn foo(maybe_num: ?u8) void {
    const num = maybe_num orelse return;
    // some incredibly complex and long task
    _ = num;
    return;
}

8 Likes

Else branch with capture is used for error values, that is why it wants an error union, but if you don’t have an error union, don’t use a capture on the else / don’t have an else. You can’t capture the payload of an optional in the else branch.

So if(maybe_num == null) return; should work too.
But orelse return is even more convenient.

1 Like

IMHO the easiest way is

pub fn foo(maybe_num: ?u8) void {
    if (maybe_num) |num| {
        _ = num;
        // TODO: something
    }
}

test foo {
    foo(null);
}

You can shorten further:

    _ = maybe_num orelse return;
    // ...

But later references shall be maybe_num.? instead of num.

Oh, I thought he wanted to discard the value intentionally, nevermind.

Welcome to ziggit @SleepyCat :slight_smile:

The problem was to find a solution that does not increase the indentation. It is uglier when you have multiple optionals.

pub fn foo(maybe_num1: ?u8, maybe_num2: ?u8) void {
    if (maybe_num1) |num1| {
        if (maybe_num2) |num2| {
            // TODO: use num1 and num2
        }
    }
}

vs

pub fn foo(maybe_num1: ?u8, maybe_num2: ?u8) void {
    const num1 = maybe_num1 orelse return;
    const num2 = maybe_num2 orelse return;
    // TODO: use num1 and num2
}
3 Likes

I got bit by this exact thing early on, I don’t think it would be that nuts if if (!optional()) { ... } else |val| {...} worked.

The wacky idea is the mandatory double else for handling !?T, which brings ‘reversal optional’ along for the ride (in a way which is compatible with the double else, for that matter).

I still don’t know if it’s a great idea or a terrible one, to be honest.