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.