Error Propagation
The ? operator provides concise syntax for propagating errors up the call stack. When applied to a Result value, it unwraps Ok values and short-circuits on Err values by returning the error from the enclosing function.
Syntax
TryExpr = Expression "?"
The ? operator is a postfix unary operator applied to an expression that evaluates to a Result.
Semantics
The ? operator evaluates its operand and inspects the result:
- If the value is
Ok(v), the expression evaluates tov(the inner value is unwrapped). - If the value is
Err(e), the enclosing function immediately returnsErr(e). - If the value is neither
OknorErr, a runtime error is raised:`?` expects Result value (Ok(...) or Err(...)).
fn parse_number(s) {
if s == "" {
return Err("empty string")
}
return Ok(int(s))
}
fn double_parsed(s) {
let n = parse_number(s)? // unwraps Ok or returns Err
return Ok(n * 2)
}
double_parsed("5") // Ok(10)
double_parsed("") // Err("empty string")
Propagation Mechanism
When ? encounters an Err value, it raises a RuntimeError with the propagated field set to the Err value. The runtime distinguishes propagated errors from ordinary runtime errors. When a propagated error reaches a function boundary, the function returns the propagated Err value rather than crashing the program.
The implementation:
Expr::Try(expr)evaluatesexpr.- If the result is
ResultOk(value), returnsvalue. - If the result is
ResultErr(err), callsRuntimeError::propagate(ResultErr(err)), which creates aRuntimeErrorwhosepropagatedfield carries the originalErrvalue. - The calling function’s error handler detects the propagated value and converts it back into a return value.
Chaining
The ? operator can be chained across multiple function calls:
fn read_config() {
let text = read_file("config.json")?
let parsed = json.parse(text)?
return Ok(parsed)
}
Each ? either unwraps the Ok value for the next step or short-circuits the entire function with the first Err encountered.
Requirements
The ? operator requires that its operand evaluates to a Result value. Applying ? to a non-Result value (such as an Int, String, or null) raises a runtime error:
let x = 42? // runtime error: `?` expects Result value (Ok(...) or Err(...))
Usage Patterns
Propagate and Transform
fn load_user(id) {
let data = fetch_user_data(id)?
let user = parse_user(data)?
return Ok(user)
}
Propagate with Fallback
Combine ? with unwrap_or for partial error handling:
fn get_config_or_default() {
let config = read_config()? // propagate file errors
let timeout = unwrap_or(config.timeout, 30) // fallback for missing field
return Ok(timeout)
}
Top-Level Handling
At the top level, propagated errors become runtime errors since there is no enclosing function to return from:
// If read_config returns Err, this crashes the program
let config = read_config()?
To handle errors at the top level, use is_ok/is_err checks, unwrap_or, or try/catch blocks instead of ?.