Symbol decoder
Rust packs a lot of meaning into punctuation. Use this table when a line of AI-generated code looks like noise. Type in the filter box to find a symbol or a word, for example &, lifetime, or closure.
| 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?; |
Three symbols to learn first
Section titled “Three symbols to learn first”If you remember only three, remember these. They appear in almost every function.
&: “borrow, do not take”. A function that takes&Treads the value; the caller keeps it.?: “if this failed, return the error now”. It is Rust’s version of exception propagation, and it is visible on every line that can fail.!after a name: a macro call, not a function call.println!,vec!,format!, andassert_eq!are macros.
Reading tips
Section titled “Reading tips”- Read signatures first. In
fn f(x: T),ftakes ownership ofx. Infn f(x: &T)it borrowsx. Infn f(x: &mut T)it borrowsxto change it. The body follows from that. - Read
<...>and'alast. Generic parameters and lifetimes are usually correct if the code compiles. Skip them on the first pass, and come back if the signature looks unusually complex. - Use your editor. With rust-analyzer, hover over a name to see its type, and turn on inlay hints to see the inferred types of
letbindings. This helps more than any table.