Option and Result
Option and Result in TypeScript vs Rust
Section titled “Option and Result in TypeScript vs Rust”TypeScript uses T | null | undefined for optional values and try/catch for errors. Rust encodes both in the type system:
Option<T>— eitherSome(value)orNone. No null, no undefined.Result<T, E>— eitherOk(value)orErr(error). No exceptions.
The type system forces you to handle both cases before you can use the value.
Option — absence without null
Section titled “Option — absence without null”// TypeScript: null can silently propagatefunction findUser(id: number): User | null { ... }const user = findUser(42);console.log(user.name); // runtime crash if null
// Rust: the compiler won't let you use the value without handling Nonefn find_user(id: u32) -> Option<User> { ... }match find_user(42) { Some(user) => println!("{}", user.name), None => println!("not found"),}Result — errors as values
Section titled “Result — errors as values”// TypeScript: error can be thrown anywherefunction divide(a: number, b: number): number { if (b === 0) throw new Error("division by zero"); return a / b;}
// Rust: error is part of the return typefn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err("division by zero".to_string()) } else { Ok(a / b) }}Convenience methods
Section titled “Convenience methods”| TypeScript | Rust |
|---|---|
value ?? defaultVal | option.unwrap_or(defaultVal) |
value! (non-null assertion) | option.unwrap() (panics if None) |
value ?? (() => { throw ... })() | option.expect("msg") (panics with message) |
The ? operator
Section titled “The ? operator”In a function that returns Result, ? on a Result value either unwraps Ok or returns the Err early — eliminating nested match chains:
fn read_number(s: &str) -> Result<i32, std::num::ParseIntError> { let n = s.trim().parse::<i32>()?; // returns Err early if parse fails Ok(n * 2)}// Option-like: null / undefinedfunction divide(a: number, b: number): number | null { if (b === 0) return null; return a / b;}const r = divide(10, 2) ?? 0;console.log(r); // 5
// Error handling: try/catchtry { const val = JSON.parse("bad json");} catch (e) { console.error("parse error", e);}fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err(String::from("division by zero")) } else { Ok(a / b) }}
fn main() { match divide(10.0, 2.0) { Ok(v) => println!("{v}"), // 5 Err(e) => println!("{e}"), }
let maybe: Option<i32> = Some(42); let value = maybe.unwrap_or(0); println!("{value}"); // 42}Try it
Section titled “Try it”fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err(String::from("division by zero")) } else { Ok(a / b) }}
fn main() { match divide(10.0, 2.0) { Ok(result) => println!("10 / 2 = {result}"), Err(e) => println!("Error: {e}"), } match divide(5.0, 0.0) { Ok(result) => println!("5 / 0 = {result}"), Err(e) => println!("Error: {e}"), } let maybe: Option<i32> = Some(42); let value = maybe.unwrap_or(0); println!("Option value: {value}");}Compiling…