Skip to content

Catch

Exhaustively recover from or convert the error side of a Result expression.

Updated View as Markdown

Postfix catch handles the Err side of a result expression.

name := load_name(id) catch {
    NotFound { id: _ } => "unknown"
    PermissionDenied => "private"
    Storage { message: _ } => "unavailable"
}

The successful payload flows through without entering an arm.

The type of the complete expression depends on what every normally completing arm produces:

Arm outcome Catch expression
success-compatible fallback recovered success value
target error value Result with the target error type
error target exit success value on the continuing path
diverging exit such as return joined from the remaining paths

All arms are still checked, even when runtime input selects only one.

Recovery

When every arm produces a value compatible with the success payload, catch recovers to that value.

config := read_config() catch {
    Missing => Config.default()
    Invalid { message } => fallback_config(message)
}

After complete recovery, config is a Config, not a Result.

This is appropriate when the current layer has a valid substitute for every failure.

Conversion

Catch arms can produce variants of a new error type.

converted := authenticate(token) catch {
    InvalidToken => RequestError.Unauthorized
    Expired => RequestError.Unauthorized
}

The expression remains a result with the original success type and the mapped error type. Add ? to convert and immediately propagate.

user := authenticate(token) catch {
    InvalidToken => RequestError.Unauthorized
    Expired => RequestError.Unauthorized
}?

Every normally completing conversion arm must agree on one target error type, or on a compatible error union. Returning a mixture of recovery values and error values does not silently guess which channel each value belongs to; use error, Ok, or Err when the intended control flow would otherwise be ambiguous.

Direct error exits

An arm can use error to leave the enclosing fallible function.

value := source() catch {
    Bad => error Mapped
    Other => error OtherMapped
}

The success payload becomes value; each failure arm exits rather than producing a replacement result.

Patterns

Catch arms use the same patterns as match.

result := request() catch {
    Timeout => retry_later()
    Invalid { field, message } => report(field, message)
    error: BackendError => map_backend(error)
}

Pattern bindings are visible in the arm guard and body.

The catch subject must be a Result value, including the T ! E signature spelling. catch is not an optional fallback operator; use ??, a pattern, or Option methods for None.

Exhaustiveness

Every reachable error variant must be covered.

value := source() catch {
    Bad => fallback()
    // error if `Other` is also possible
}

Use _ or a type-group binding only when treating the remaining failures uniformly is intentional.

value := source() catch {
    NotFound => fallback()
    _ => unavailable()
}

Guards

Catch patterns may use guards where the match grammar permits them.

value := source() catch {
    Backend { code } if code == 409 => retry()
    Backend { code: _ } => fail()
}

An unguarded arm is still required for any cases not proven covered.

Guards refine selection but do not remove a variant from the exhaustiveness set. Several guarded arms for one variant still need an unguarded arm capable of accepting that variant.

Catch versus match

Use catch when the success path should pass through unchanged and only the error side needs attention.

Use match when success and failure both need explicit handling or when the result should remain visibly two-channel in local control flow.

match load_user(id) {
    Ok(user) => show(user)
    Err(error) => show_error(error)
}

Method form

Result also exposes functional error transformation. The keyword form is preferred when variant patterns and exhaustiveness are central; a method form is useful in higher-order pipelines.

Neither form catches runtime traps, host-process termination, or arbitrary exceptions. It transforms a typed Err value.

Evaluation

The subject is evaluated once. On Ok, the payload passes through and no catch arm runs. On Err, arms are tried in source order using ordinary match selection. Guards on pattern-matching arms may run until one succeeds; only the selected arm body runs. Keep guards free of effects whose repetition or order would be surprising.

Cleanup registered before the catch subject runs still obeys the enclosing scope’s normal exit rules. A direct error or return from an arm is an early exit and therefore runs applicable defers.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close