← Back to AILP Home

? Fallback Unwrap

The source form is:

result_expr ? fallback_expr

For Result<T>, ? yields the success value when the receiver succeeds and the explicit fallback when the receiver is an error.

Input Output
Result(_, V, false) ? F V
Result(_, _, true) ? F F

Example

func:maybe_number = int32(bool:ok) {
    if (ok) {
        pass 42i32;
    };
    fail 7i32;
};

func:main = int32() {
    int32:a = maybe_number(true) ? 0i32;   // 42
    int32:b = maybe_number(false) ? 0i32;  // 0
    exit a - b;
};

Not propagation

? is not Rust-style propagation. It does not automatically return from the current function on error. The fallback is mandatory and local.

int32:x = might_fail() ? 10i32;  // local fallback

If the fallback should depend on the error code, use contextual catch:

int32:x = might_fail() catch (err) { err + 10i32 };

Type checking

The fallback expression must match the unwrapped value type T, or be coercible under the ordinary literal/assignment rules.

int32:x = maybe_number(false) ? 0i32;  // OK
// int32:y = maybe_number(false) ? "no";  // type error

Nested fallback is explicit

Nested Results keep their own state until you handle them:

func:inner = int32() { fail 4i32; };

func:outer = int32() {
    int32:x = inner() ? 14i32;
    pass x;
};

The inner() failure does not silently become an outer() failure.

await shape

v0.33.1 locked this parser shape:

await work() ? fallback  ==  (await work()) ? fallback

The await operand consumes call/member/index postfixes, but leaves Result fallback operators for the outer expression.