โข Option
โข Result
โข The ? operator propagates errors up the call stack concisely
โข Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations
โข The compiler forces you to handle every error case โ no silent failures
โข Option
โข Result
โข The ? operator propagates errors up the call stack concisely
โข Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations
โข The compiler forces you to handle every error case โ no silent failures
// Functions returning ! never return normally
fn crash(msg: &str) -> ! {
panic!("{}", msg)
}
// ! coerces to any type โ valid in match arms
fn parse_or_die(s: &str) -> i32 {
s.parse().unwrap_or_else(|_| crash("parse failed"))
// ^^^^^^^^^^^^^^^^^ : ! coerces to i32
}
// Infallible: can never be constructed
use std::convert::Infallible;
let r: Result<i32, Infallible> = Ok(42);
let val = r.unwrap(); // safe โ Err variant is impossible
// Result<T, Infallible> can match with only the Ok arm
let val = match r {
Ok(v) => v,
// Err arm omitted โ compiler knows Infallible has no values
};
// From<!> for T is implemented for all T
// From<Infallible> for T is implemented via the same logic
| Concept | OCaml | Rust |
|---|---|---|
| Never type | `'a` (universal polymorphism) | `!` (explicit bottom type) |
| Diverging coercion | Implicit (any diverging expr) | `!` coerces to any type |
| Infallible error | Phantom type / `exn` workaround | `std::convert::Infallible` |
| Match on empty type | Impossible pattern | `match inf {}` (zero arms) |
| Process exit | `exit` returns `unit` | `std::process::exit() -> !` |
//! 309. The ! (never) type in error handling
//!
//! `!` is the never/bottom type: diverging functions, `Infallible`, exhaustive matches.
use std::convert::Infallible;
/// A function that never returns โ return type `!`
fn crash(msg: &str) -> ! {
panic!("{}", msg)
}
/// `!` coerces to any type โ useful in match arms
fn parse_or_crash(s: &str) -> i32 {
s.parse::<i32>().unwrap_or_else(|e| crash(&format!("fatal parse error: {}", e)))
}
/// Result<T, Infallible> can only be Ok โ infallible conversion
fn to_uppercase(s: &str) -> Result<String, Infallible> {
Ok(s.to_uppercase())
}
/// From<Infallible> coercion: convert Result<T, Infallible> to T
fn infallible_result() {
let r: Result<i32, Infallible> = Ok(42);
// Since Infallible has no values, we can exhaustively match with only Ok arm
let val = match r {
Ok(v) => v,
// Err(e) => match e {} // would need this, but Infallible has no variants
};
println!("Infallible result: {}", val);
// Or use unwrap โ can never panic since Err is impossible
let val2: i32 = Ok::<i32, Infallible>(99).unwrap();
println!("unwrap on Infallible: {}", val2);
}
/// The never type in match arms (! coerces to anything)
fn process(s: &str) -> i32 {
if let Ok(n) = s.parse::<i32>() {
n
} else {
crash("this input is always a number") // -> ! coerces to i32
}
}
fn main() {
println!("parse_or_crash('42') = {}", parse_or_crash("42"));
let upper: Result<String, Infallible> = to_uppercase("hello");
println!("Infallible: {:?}", upper);
// Convert Infallible result to value (always safe)
let val: String = upper.unwrap_infallible();
println!("Unwrapped: {}", val);
infallible_result();
// Never type in closures
let _f: fn() -> ! = || panic!("never");
}
#[cfg(test)]
mod tests {
use super::*;
use std::convert::Infallible;
#[test]
fn test_infallible_is_ok() {
let r: Result<i32, Infallible> = Ok(42);
assert!(r.is_ok());
assert_eq!(r.unwrap(), 42);
}
#[test]
fn test_to_uppercase_infallible() {
let r = to_uppercase("rust");
assert_eq!(r.unwrap(), "RUST");
}
#[test]
fn test_parse_or_crash() {
assert_eq!(parse_or_crash("100"), 100);
}
#[test]
#[should_panic]
fn test_crash_panics() {
crash("intentional crash");
}
}
// Extension trait for unwrap_infallible
trait UnwrapInfallible<T> {
fn unwrap_infallible(self) -> T;
}
impl<T> UnwrapInfallible<T> for Result<T, Infallible> {
fn unwrap_infallible(self) -> T {
match self {
Ok(v) => v,
}
}
}
(* 309. The never type in error handling - OCaml *)
(* OCaml: diverging functions have type 'a (polymorphic) *)
(* A function that never returns (loops) *)
let rec loop () : 'a = loop ()
(* A function that always panics *)
let abort msg : 'a = failwith msg
(* Simulating Infallible with an uninhabited type *)
type infallible = | (* empty variant -- OCaml 4.11+ *)
let to_result : int -> (int, infallible) result = fun n -> Ok n
let () =
let r = to_result 42 in
(* exhaustive match: Err case is impossible *)
let v = match r with
| Ok n -> n
| Error _ -> assert false (* dead code *)
in
Printf.printf "Value: %d\n" v;
(* Using option to simulate ? with never-returning branches *)
let find_first pred lst =
match List.find_opt pred lst with
| Some v -> v
| None -> failwith "precondition violated: element must exist"
in
let v = find_first (fun x -> x > 3) [1; 2; 3; 4; 5] in
Printf.printf "First > 3: %d\n" v