Rust has no null and no exceptions. Option<T> represents a value that may be absent. Result<T,E> represents success or failure. The ? operator propagates errors ergonomically.
Docs: doc.rust-lang.org
| Name | Signature | Description |
|---|---|---|
| Option::Some(v) | Some(v): Option<T> | A present value |
| Option::None | None: Option<T> | An absent value |
| opt.unwrap | fn unwrap(self) -> T | Return value or panic if None |
| opt.unwrap_or | fn unwrap_or(self, default: T) -> T | Return value or default if None |
| opt.unwrap_or_else | fn unwrap_or_else(self, f: F) -> T | Return value or call f() if None |
| opt.map | fn map<U>(self, f: F) -> Option<U> | Transform Some value, pass None through |
| opt.and_then | fn and_then<U>(self, f: F) -> Option<U> | Chain Option-returning operations (flatMap) |
| opt.or | fn or(self, optb: Option<T>) -> Option<T> | Return self if Some else optb |
| opt.filter | fn filter<P>(self, predicate: P) -> Self | Keep Some if predicate holds |
| opt.is_some | fn is_some(&self) -> bool | True if Some |
| opt.is_none | fn is_none(&self) -> bool | True if None |
| opt.as_ref | fn as_ref(&self) -> Option<&T> | Convert &Option<T> to Option<&T> |
| opt.take | fn take(&mut self) -> Option<T> | Take value out, leaving None |
| opt.ok_or | fn ok_or<E>(self, err: E) -> Result<T,E> | Convert Some to Ok, None to Err(err) |
| opt.flatten | fn flatten(self) -> Option<T> where T: Option<U> | Flatten Option<Option<T>> to Option<T> |
| Result::Ok(v) | Ok(v): Result<T,E> | Success value |
| Result::Err(e) | Err(e): Result<T,E> | Error value |
| res.unwrap | fn unwrap(self) -> T | Return Ok value or panic |
| res.unwrap_or | fn unwrap_or(self, default: T) -> T | Return Ok value or default |
| res.map | fn map<U,F>(self, op: F) -> Result<U,E> | Transform Ok value |
| res.map_err | fn map_err<F,O>(self, op: O) -> Result<T,F> | Transform Err value |
| res.and_then | fn and_then<U,F>(self, op: F) -> Result<U,E> | Chain Result-returning operations |
| res.is_ok | fn is_ok(&self) -> bool | True if Ok |
| res.is_err | fn is_err(&self) -> bool | True if Err |
| ? operator | expr? : propagate Err/None | Return Err/None from current function if result is Err/None |
| res.ok() | fn ok(self) -> Option<T> | Convert Ok to Some, Err to None |
| From / Into | trait From<T> for U | Auto-conversion used by ? operator to convert error types |
Save as main.rs in a Cargo project and run with cargo run.
use std::num::ParseIntError;
use std::fmt;
#[derive(Debug)]
enum AppError {
Parse(ParseIntError),
Validation(String),
Io(std::io::Error),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::Parse(e) => write!(f, "parse: {}", e),
AppError::Validation(s) => write!(f, "validation: {}", s),
AppError::Io(e) => write!(f, "I/O: {}", e),
}
}
}
impl From<ParseIntError> for AppError { fn from(e: ParseIntError) -> Self { AppError::Parse(e) } }
impl From<std::io::Error> for AppError { fn from(e: std::io::Error) -> Self { AppError::Io(e) } }
fn parse_age(s: &str) -> Result<u8, AppError> {
let n: i32 = s.trim().parse::<i32>()?; // ? converts ParseIntError via From
if n < 0 || n > 150 {
return Err(AppError::Validation(format!("{} out of [0,150]", n)));
}
Ok(n as u8)
}
fn find_user(name: &str) -> Option<(&str, u8)> {
[("Alice", 30u8), ("Bob", 25), ("Carol", 35)].iter().find(|(n,_)| *n == name).copied()
}
fn main() {
// Result handling
for input in &["25", "abc", "-1", "200"] {
match parse_age(input) {
Ok(age) => println!("Valid age: {}", age),
Err(e) => println!("Error: {}", e),
}
}
// Option chaining
for name in &["Alice", "Dave", "Carol"] {
let msg = find_user(name)
.map(|(n, age)| format!("{} is {} years old", n, age))
.unwrap_or_else(|| format!("{} not found", name));
println!("{}", msg);
}
// ? in closure returning Result
let result: Result<i32, AppError> = (|| {
let content = std::fs::read_to_string("missing.txt")?;
let n: i32 = content.trim().parse::<i32>()?;
Ok(n * 2)
})();
println!("File result: {:?}", result);
}
// cargo run