unwrap() / expect() in non-test code
The program stops (panics) on a failure that the caller could handle.
This page is not in the sidebar or in search. It shows each component with sample data, so you can check them in light mode, dark mode, and on a narrow screen. The sample code is in examples/demo/ and examples/java/demo/.
With a title, a line highlight, and a lines marked // HIDE in the source (the #[derive] line is not shown):
pub struct OrderLine { pub unit_price_cents: u64, pub quantity: u32,}
#[must_use]pub fn order_total(lines: &[OrderLine]) -> u64 { lines .iter() .map(|line| line.unit_price_cents * u64::from(line.quantity)) .sum()}A region inside an inline module. The snippet is dedented and has a “Run in Playground” link because the crate uses only std:
#[must_use]pub fn find_user(users: &[User], id: u64) -> Option<&User> { users.iter().find(|u| u.id == id)}A compile_fail doc test. CI proves that it fails with error E0382:
let orders = vec!["A-100".to_string(), "A-101".to_string()];let archived = orders; // the Vec moves to `archived`println!("{}", orders.len()); // error: borrow of moved valuestatic Optional<User> findUser(List<User> users, long id) { return users.stream() .filter(u -> u.id() == id) .findFirst();}Java
static Optional<User> findUser(List<User> users, long id) { return users.stream() .filter(u -> u.id() == id) .findFirst();}Rust
#[must_use]pub fn find_user(users: &[User], id: u64) -> Option<&User> { users.iter().find(|u| u.id == id)}WorsePanics when the user is missing, copies the user, and takes &Vec<User>.
pub fn find_user(users: &Vec<User>, id: u64) -> User { for i in 0..users.len() { if users[i].id == id { return users[i].clone(); } } panic!("user {id} not found");}OKReturns Option, but clones the user that it found.
#[must_use]pub fn find_user(users: &[User], id: u64) -> Option<User> { users.iter().find(|u| u.id == id).cloned()}BetterReturns Option<&User>: no panic and no copy.
#[must_use]pub fn find_user(users: &[User], id: u64) -> Option<&User> { users.iter().find(|u| u.id == id)}| Criterion | OK | Better |
|---|---|---|
| Correctness | Better | Better |
| No panic for a missing user. | ||
| Performance | Same | Better |
| "OK" still clones the user. | ||
| Readability | Better | Better |
pub struct OrderLine { pub unit_price_cents: u64, pub quantity: u32,}
#[must_use]pub fn order_total(lines: &[OrderLine]) -> u64 { lines .iter() .map(|line| line.unit_price_cents * u64::from(line.quantity)) .sum()}struct holds data only. Behavior goes in impl blocks or free functions.&[OrderLine] borrows a slice. The function reads the lines but does not own them.sum() is the terminal operation.unwrap() / expect() in non-test codeThe program stops (panics) on a failure that the caller could handle.
clone() to satisfy the borrow checkerA copy of the data is made only to make a borrow error go away.
unwrap() / expect() in non-test codeThe program stops (panics) on a failure that the caller could handle.Result<T, String> or stringly-typed errorsThe caller cannot match on the kind of error, only read a message.Result (let _ =, .ok();)An error is thrown away silently.map_err(|_| ...))The original error is replaced, so the cause is no longer available.as casts that can truncate or change signA numeric conversion that silently produces a wrong value.panic! used for normal errorsAn expected failure stops the program instead of returning an error.What does Option<&User> tell the caller?
There can be no user. If there is one, the caller borrows it and does not own it.
Why does the 'worse' version take &Vec<User>?
Probably a direct translation of List<User>. A slice, &[User], accepts more types and gives the same access.
| Symbol | Name | Meaning | Example |
|---|---|---|---|
& | Shared reference (borrow) | Borrow a value to read it. Many shared references can exist at the same time. In a type, &T means "a reference to a T". | fn total(orders: &[Order]) -> u64 |
&mut | Mutable reference | Borrow a value to change it. While it exists, no other reference to the value can exist. | fn add_line(order: &mut Order, line: Line) |
* | Dereference | Read or write the value behind a reference or smart pointer. Often not needed, because method calls dereference automatically. | *count += 1; |
'a | Lifetime parameter | A name for how long a reference is valid. It describes, it does not change how long a value lives. | fn longer<'a>(a: &'a str, b: &'a str) -> &'a str |
'static | Static lifetime | On a reference: valid for the whole program (for example a string literal). As a bound T: 'static: the type holds no short-lived references. It does not mean "lives forever". | const GREETING: &'static str = "Hello"; |
? | Question mark operator | If the value is an Err (or None), return it to the caller now. If not, unwrap the value and continue. Converts the error type with From. | let text = fs::read_to_string(path)?; |
name! | Macro call | A ! after a name calls a macro, not a function. Macros generate code at compile time. | println!("{} orders", orders.len()); |
! | Never type | In a return type: the function never returns (it panics, loops forever, or exits the process). | fn fail(msg: &str) -> ! { panic!("{msg}") } |
:: | Path separator | Separates modules, types, and associated items. Like . in a Java package name or a static member access. | let orders = Vec::new(); |
-> | Return type | The type that a function or closure returns. No -> means the function returns () (like void). | fn count(orders: &[Order]) -> usize |
=> | Match arm | Separates a pattern from the code that runs when the pattern matches. Like -> in a Java switch expression. | Status::Paid => "paid", |
|x| | Closure parameters | Starts a closure (a lambda). |o| o.total is Java o -> o.total(). || is a closure with no parameters. | orders.iter().filter(|o| o.total_cents > 0) |
.. | Range, or "the rest" | Exclusive range a..b (end not included). In a pattern or struct literal: "ignore the other fields" or "take the other fields from here". | for i in 0..3 {} // 0, 1, 2 |
..= | Inclusive range | A range that includes the end value. Also used in patterns. | 1..=5 => "small", |
_ | Wildcard / placeholder | In a pattern: match anything and ignore it. In a type: "infer this". As a name prefix (_order): "unused on purpose". | let ids: Vec<_> = orders.iter().map(|o| o.id).collect(); |
#[...] | Attribute | Metadata on the next item, like a Java annotation. #[derive(...)] generates trait implementations. | #[derive(Debug, Clone, PartialEq)] |
#![...] | Inner attribute | An attribute that applies to the item it is inside (the whole module or crate), not to the next item. | #![forbid(unsafe_code)] |
<T> | Generic type parameter | A type parameter, as in Java. Rust generates a separate copy of the code for each type used (no type erasure). | struct Page<T> { items: Vec<T>, total: usize } |
<T: Trait> | Trait bound | T must implement the trait. Like <T extends Comparable<T>> in Java. | fn largest<T: Ord>(items: &[T]) -> Option<&T> |
dyn | Trait object | A value whose concrete type is known only at run time; calls go through a vtable. This is how every Java interface call works. | let handlers: Vec<Box<dyn Handler>> = Vec::new(); |
impl | Implementation block, or "some type that implements" | impl Type { } adds methods. impl Trait for Type { } implements an interface. impl Trait in a signature means "one concrete type that implements the trait". | fn ids(&self) -> impl Iterator<Item = u64> + '_ |
as | Primitive cast, or rename | Converts between number types. It can truncate or change the sign without an error. Also renames an import. | let small = big as u8; // 300 becomes 44 |
mut | Mutable | The binding can change. Variables are immutable by default, the opposite of Java. | let mut total = 0; |
ref | Bind by reference | In a pattern: borrow the matched value instead of moving it. Modern code rarely needs it. | if let Some(ref name) = customer.nickname {} |
move | Move closure | The closure takes ownership of the variables that it uses. Needed for threads and async tasks. | thread::spawn(move || process(orders)); |
Self / self | The type / the receiver | Self is the type in the current impl block. self is the value a method is called on, like this. | fn new(id: u64) -> Self { Self { id } } |
r#"..."# | Raw string literal | A string with no escape processing. Useful for JSON, regular expressions, and Windows paths. Like a Java text block without escapes. | let json = r#"{"id": 7}"#; |
b"..." | Byte string literal | A byte array (&[u8; N]), not a UTF-8 str. b'a' is one byte. | socket.write_all(b"PING\r\n")?; |
1_000 | Digit separator | Underscores in number literals are ignored. Same as in Java. | let limit_cents = 1_000_000; |
0u8 | Literal type suffix | The suffix gives the type of the literal: u8, i64, f32, usize, and so on. | let retries = 3u32; |
@ | Pattern binding | Give a name to a value and also test it against a pattern. | n @ 1..=9 => format!("single digit {n}"), |
'label: | Loop label | Names a loop, so that break or continue can target an outer loop. Same idea as Java labels. | 'orders: for order in &orders { break 'orders; } |
::<> | Turbofish | Gives a generic type argument at the call site, when the compiler cannot infer it. | let n = "42".parse::<u32>()?; |
where | Where clause | Trait bounds written after the signature, for readability. Same meaning as bounds in <...>. | fn report<T>(items: &[T]) -> String where T: Display |
unsafe | Unsafe block or function | Code where you, not the compiler, must prove memory safety. Every unsafe block needs a // SAFETY: comment. | unsafe { ptr.read() } |
async | Async function or block | Returns a Future. Nothing runs until something awaits it (unlike CompletableFuture, which starts at once). | async fn fetch_order(id: u64) -> Result<Order, Error> |
.await | Await | Waits for a future to complete, without blocking the thread. A postfix keyword, so it chains: .await?. | let order = fetch_order(7).await?; |