Skip to content

Component demo

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):

order_total.rs
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()
}
Run in Playground (opens the Rust Playground in a new tab)

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)
}
Run in Playground (opens the Rust Playground in a new tab)

A compile_fail doc test. CI proves that it fails with error E0382:

Does not compile: 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 value
FindUser.java
static 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)
}
Run in Playground (opens the Rust Playground in a new tab)
  1. 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");
    }
    Run in Playground (opens the Rust Playground in a new tab)
  2. 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()
    }
    Run in Playground (opens the Rust Playground in a new tab)
  3. 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)
    }
    Run in Playground (opens the Rust Playground in a new tab)
Each version compared with the Worse version
CriterionOKBetter
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()
}
  1. Line 1: A struct holds data only. Behavior goes in impl blocks or free functions.
  2. Line 7: &[OrderLine] borrows a slice. The function reads the lines but does not own them.
  3. Line 8-11: An iterator chain, like a Java stream. sum() is the terminal operation.
  1. What does Option<&User> tell the caller?

    Show the answer

    There can be no user. If there is one, the caller borrows it and does not own it.

  2. Why does the 'worse' version take &Vec<User>?

    Show the answer

    Probably a direct translation of List<User>. A slice, &[User], accepts more types and gives the same access.

SymbolNameMeaningExample
&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
&mutMutable referenceBorrow a value to change it. While it exists, no other reference to the value can exist.fn add_line(order: &mut Order, line: Line)
*DereferenceRead or write the value behind a reference or smart pointer. Often not needed, because method calls dereference automatically.*count += 1;
'aLifetime parameterA 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
'staticStatic lifetimeOn 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 operatorIf 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 callA ! after a name calls a macro, not a function. Macros generate code at compile time.println!("{} orders", orders.len());
!Never typeIn a return type: the function never returns (it panics, loops forever, or exits the process).fn fail(msg: &str) -> ! { panic!("{msg}") }
::Path separatorSeparates modules, types, and associated items. Like . in a Java package name or a static member access.let orders = Vec::new();
->Return typeThe type that a function or closure returns. No -> means the function returns () (like void).fn count(orders: &[Order]) -> usize
=>Match armSeparates a pattern from the code that runs when the pattern matches. Like -> in a Java switch expression.Status::Paid => "paid",
|x|Closure parametersStarts 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 rangeA range that includes the end value. Also used in patterns.1..=5 => "small",
_Wildcard / placeholderIn 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();
#[...]AttributeMetadata on the next item, like a Java annotation. #[derive(...)] generates trait implementations.#[derive(Debug, Clone, PartialEq)]
#![...]Inner attributeAn 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 parameterA 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 boundT must implement the trait. Like <T extends Comparable<T>> in Java.fn largest<T: Ord>(items: &[T]) -> Option<&T>
dynTrait objectA 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();
implImplementation 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> + '_
asPrimitive cast, or renameConverts 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
mutMutableThe binding can change. Variables are immutable by default, the opposite of Java.let mut total = 0;
refBind by referenceIn a pattern: borrow the matched value instead of moving it. Modern code rarely needs it.if let Some(ref name) = customer.nickname {}
moveMove closureThe closure takes ownership of the variables that it uses. Needed for threads and async tasks.thread::spawn(move || process(orders));
Self / selfThe type / the receiverSelf 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 literalA 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 literalA byte array (&[u8; N]), not a UTF-8 str. b'a' is one byte.socket.write_all(b"PING\r\n")?;
1_000Digit separatorUnderscores in number literals are ignored. Same as in Java.let limit_cents = 1_000_000;
0u8Literal type suffixThe suffix gives the type of the literal: u8, i64, f32, usize, and so on.let retries = 3u32;
@Pattern bindingGive a name to a value and also test it against a pattern.n @ 1..=9 => format!("single digit {n}"),
'label:Loop labelNames a loop, so that break or continue can target an outer loop. Same idea as Java labels.'orders: for order in &orders { break 'orders; }
::<>TurbofishGives a generic type argument at the call site, when the compiler cannot infer it.let n = "42".parse::<u32>()?;
whereWhere clauseTrait bounds written after the signature, for readability. Same meaning as bounds in <...>.fn report<T>(items: &[T]) -> String where T: Display
unsafeUnsafe block or functionCode where you, not the compiler, must prove memory safety. Every unsafe block needs a // SAFETY: comment.unsafe { ptr.read() }
asyncAsync function or blockReturns a Future. Nothing runs until something awaits it (unlike CompletableFuture, which starts at once).async fn fetch_order(id: u64) -> Result<Order, Error>
.awaitAwaitWaits for a future to complete, without blocking the thread. A postfix keyword, so it chains: .await?.let order = fetch_order(7).await?;