Operator
The ? operator (the try operator) is a postfix unary operator that unwraps a successful value (Ok(T) or Some(T)) and evaluates to T, or returns early from the enclosing function with the failure (Err(E) or None).
How expr? Evaluates
When placed after a Result<T, E> expression inside a function returning Result<U, F>, expr? expands conceptually to a match expression that converts the error via From::from:
let val = fallible_call()?;
│
├── Ok(v) ──> binds `val = v` and continues execution
└── Err(e) ──> returns `Err(From::from(e))` from the current function
// Using `?`:
fn parse_pair_question(a: &str, b: &str) -> Result<(i32, i32), std::num::ParseIntError> {
let x = a.parse::<i32>()?;
let y = b.parse::<i32>()?;
Ok((x, y))
}
// Equivalent manual `match`:
fn parse_pair_match(a: &str, b: &str) -> Result<(i32, i32), std::num::ParseIntError> {
let x = match a.parse::<i32>() {
Ok(v) => v,
Err(e) => return Err(From::from(e)),
};
let y = match b.parse::<i32>() {
Ok(v) => v,
Err(e) => return Err(From::from(e)),
};
Ok((x, y))
}
fn main() {
println!("{:?}", parse_pair_question("10", "25"));
println!("{:?}", parse_pair_match("10", "bad"));
}
Ok((10, 25))
Err(ParseIntError { kind: InvalidDigit })
Chaining Method Calls with ?
Because ? is a postfix operator (written after the expression rather than before it), multiple fallible operations can be chained left-to-right in a single expression:
use std::num::ParseIntError;
fn sum_first_two_csv(line: &str) -> Result<i32, ParseIntError> {
let mut parts = line.split(',');
let first = parts.next().unwrap_or("0").trim().parse::<i32>()?;
let second = parts.next().unwrap_or("0").trim().parse::<i32>()?;
Ok(first + second)
}
fn main() {
println!("Valid: {:?}", sum_first_two_csv(" 14, 28, 42 "));
println!("Invalid: {:?}", sum_first_two_csv(" 14, oops, 42 "));
}
Valid: Ok(42)
Invalid: Err(ParseIntError { kind: InvalidDigit })
Using ? on Option<T>
Inside a function that returns Option<U>, applying ? to an Option<T> unwraps Some(v) or returns None immediately on the first missing value:
fn middle_initial(full_name: &str) -> Option<char> {
let mut words = full_name.split_whitespace();
let _first = words.next()?;
let middle = words.next()?;
let _last = words.next()?;
middle.chars().next()
}
fn main() {
println!("Three words: {:?}", middle_initial("Ada Lovelace Byron"));
println!("Two words: {:?}", middle_initial("Alan Turing"));
}
Three words: Some('L')
Two words: None
Return-Type Compatibility Rules
The ? operator can only be used inside a function (or closure) whose return type is compatible with the expression being tried:
Expression Before ? |
Required Enclosing Return Type |
|---|---|
Result<T, E> |
Result<U, F> where F: From<E> |
Option<T> |
Option<U> |
ops::ControlFlow<B, C> |
ops::ControlFlow<B, D> |
Using ? in a function that returns () or mixing Option<T> and Result<T, E> directly without converting (opt.ok_or(err)? or res.ok()?) produces error E0277:
// ❌ does not compile
fn log_number(s: &str) {
let n: i32 = s.parse()?;
println!("{n}");
}
fn main() {
log_number("42");
}
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> main.rs:2:27
|
1 | fn log_number(s: &str) {
| ---------------------- this function should return `Result` or `Option` to accept `?`
2 | let n: i32 = s.parse()?;
| ^ cannot use the `?` operator in a function that returns `()`