Rust has no exceptions. Instead, a function that can fail returns Result<T, E>, an enum with Ok(T) for success and Err(E) for failure. Because it's an ordinary type, the compiler nudges the caller to handle both cases: Result is marked #[must_use], so calling a Result-returning function and dropping the value produces an "unused Result that must be used" warning (not an error — the code still compiles and runs, silently discarding an Err at runtime if there is one). That nudge can also be switched off entirely and on purpose: writing let _ = might_fail(); discards the Result with no warning at all. So it's not literally impossible to silently ignore a possible error the way it's impossible to, say, use a moved value — it's a lint you're expected to heed, not a wall you can't get past.
Result<T, E> and match
The standard library defines Result as an ordinary two-variant enum, handled the same way as any other enum — typically with match.
// enum Result<T, E> { Ok(T), Err(E) }
use std::fs::File;
let f = File::open("hello.txt");
match f {
Ok(file) => println!("opened: {:?}", file),
Err(error) => println!("failed: {:?}", error),
}The ? operator: one character for error propagation
? after a Result-returning expression unwraps Ok automatically, or returns early with the Err — converting it via From if the error types differ. This replaces what would otherwise be a repetitive match-and-early-return in every fallible function.
use std::fs::File;
use std::io::{self, Read};
fn read_username() -> Result<String, io::Error> {
let mut f = File::open("username.txt")?; // returns early on Err
let mut s = String::new();
f.read_to_string(&mut s)?; // same here
Ok(s)
}unwrap, expect, and when they're acceptable
unwrap() panics immediately on Err, extracting the Ok value or crashing; expect("message") does the same with a custom panic message. Idiomatic Rust reserves these for cases where failure genuinely means a bug (prototypes, tests, or an invariant that truly cannot fail) rather than production error handling, where ? or an explicit match is preferred.
let f = File::open("hello.txt").unwrap(); // panics if the file is missing
let f = File::open("hello.txt").expect("hello.txt should exist"); // panics with a custom message