Rust syntax cheat sheet
Print this page: use your browser’s Print command (Ctrl+P or Cmd+P), or choose “Save as PDF” in the print dialog. Every error code, and the program behind each fix, comes from a run in Rust 1.99.1, edition 2024; the collapsed “Runs” boxes show the programs and what they printed. Cells with .. or ..., and the signature without its body, are elided placeholders, not runs. This sheet links the lessons instead of teaching again.
Symbols
Section titled “Symbols”| Symbol | Meaning | Lesson |
|---|---|---|
let, mut |
bind a name; fixed unless mut |
Variables |
fn, -> |
define a function; the result type follows -> |
Functions |
; |
ends a statement and drops its value | Statements |
{ } |
a block; its last line, with no ;, is its value |
Statements |
( ) |
call, grouping, tuple (1, 'x'); () is “nothing” |
Tuples |
[ ] |
index v[0], array [1, 2], slice type &[T] |
Vectors |
:: |
a path: module, type or function | Signatures |
. |
a field or a method of a value | Methods |
&, &mut |
borrow to read; borrow to change | Borrowing, Mutable borrows |
* |
follow a reference *m; also multiply |
Borrowing |
'a, 'outer: |
a lifetime; a loop label | Lifetimes, Loops |
! |
a macro call (println!); also “not”: !done |
Macros and attributes |
? |
on Err or None, return it now; else the value inside. The function must return a Result or Option |
Errors |
#[...] |
an attribute for the next item | Macros and attributes |
|x| |
a closure (an inline function) | Closures |
=> |
a match arm: pattern, then result |
Match |
.., ..= |
range without or with its end; “the rest” in a pattern | Loops |
_ |
ignore this; digit separator 1_000 |
Patterns |
@ |
name a value and test it: n @ 3..=9 |
Patterns |
<T>, where |
a type parameter; its bounds after the signature | Generics |
as |
convert a number: 7 as f64 |
Declarations and types |
impl |
add methods, or implement a trait for a type | Methods, Traits |
dyn |
a trait object: the method is found at run time | Trait objects |
pub, use, mod |
visible outside; shorten a name; a module | Modules |
/// |
a doc comment on the next item | Macros and attributes |
Runs: one program that uses every symbol above
//! A crate-level `//!` doc comment.#![allow(dead_code, unused_variables)]
mod shapes { /// A doc comment on a public item (`///`). pub struct Square(pub f64);
pub trait Area { fn area(&self) -> f64; }
impl Area for Square { fn area(&self) -> f64 { self.0 * self.0 } }}
use shapes::{Area, Square};
fn biggest<T>(items: &[T]) -> &Twhere T: PartialOrd,{ let mut best = &items[0]; for item in items { if item > best { best = item; } } best}
fn longer<'a>(a: &'a str, b: &'a str) -> &'a str { if a.len() >= b.len() { a } else { b }}
fn total(shapes: &[&dyn Area]) -> f64 { shapes.iter().map(|s| s.area()).sum()}
fn parse(text: &str) -> Result<i32, std::num::ParseIntError> { let n = text.parse::<i32>()?; Ok(n)}
#[derive(Debug)]struct Point { x: i32, y: i32,}
fn main() { let mut count = 0; count += 1; let big = 1_000_000; let pair = (1, 'x'); let list = [1, 2, 3]; let first = list[0]; let has_items = !list.is_empty(); let r = &count; let m = &mut count; *m += 1; let half = 7 as f64 / 2.0; let label = 'x'; 'outer: for i in 0..3 { for j in 0..=3 { if j == 1 { continue 'outer; } } } let kind = match first { 1 | 2 => "small", n @ 3..=9 => "mid", _ => "other", }; let p = Point { x: 1, y: 2 }; let Point { x, .. } = p; let sq = Square(2.0); let sum = total(&[&sq]); println!("{} {} {} {:?} {} {} {}", count, first, half, parse("7"), kind, x, sum); println!("{} {}", has_items, longer("ab", "abc")); println!("{}", biggest(&[3, 9, 4]));}2 1 3.5 Ok(7) small 1 4 true abc 9
You write, in Rust
Section titled “You write, in Rust”Syntax errors stop at the parser and have no code. The others carry the code shown.
| You write | Code | In Rust |
|---|---|---|
x++; |
none | x += 1; |
let n = big ? 10 : 1; |
none | let n = if big { 10 } else { 1 }; |
for (let mut i = 0; i < 3; i++) |
none | for i in 0..3, (0..9).step_by(3) |
let a = null; and let b: String = None; |
E0425, E0308 | let b: Option<String> = None;, then match or if let |
let wide: i64 = small; (an i32) |
E0308 | i64::from(small), f64::from(small) * 1.5 |
area(width = 3, height = 4) |
E0425 | a struct: Rect { width: 3, height: 4 } |
two fn show, one per type |
E0428, E0308 | one trait, impl Show for i32, impl Show for &str |
switch (n) { case 1: ... break; } |
none | match n { 1 => .., 2 | 3 => .., _ => .. } |
try { ... } catch (e) { ... } |
none | return a Result, pass it up with ? |
if count { ... } (an integer) |
E0308 | count != 0, !items.is_empty() |
let first = s[0]; (a String) |
E0277 | s.chars().next(), s.as_bytes()[0], &s[0..1] (panics inside a character) |
n == 1.0 (n an integer) |
E0277, E0308 | n == 1, or f64::from(n) == 1.0 (n an i32) |
a === b |
none | ==, between two values of one type |
Taught in what Rust doesn’t have, with the null, string indexing and overloading details in their own lessons. All codes: error index.
Runs: each failing line compiled, and the fixes
$ for f in fails/*.rs; do rustc --edition 2024 $f; done # first lines of each error
== fails/e01.rs
error[E0308]: mismatched types
== fails/h01.rs
error: Rust has no postfix increment operator
== fails/h02.rs
error: Rust has no ternary operator
== fails/h03.rs
error: expected pattern, found `let`
error: expected one of `)`, `,`, `@`, `if`, or `|`, found `=`
== fails/h04.rs
error[E0425]: cannot find value `null` in this scope
error[E0308]: mismatched types
== fails/h05.rs
error[E0308]: mismatched types
== fails/h06.rs
error[E0425]: cannot find value `width` in this scope
error[E0425]: cannot find value `height` in this scope
== fails/h07.rs
error[E0428]: the name `show` is defined multiple times
error[E0308]: mismatched types
== fails/h08.rs
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
== fails/h09.rs
error: keyword `catch` cannot follow a `try` block
== fails/h10.rs
error[E0308]: mismatched types
== fails/h11.rs
error[E0277]: the type `str` cannot be indexed by `{integer}`
== fails/h12.rs
error[E0277]: can't compare `{integer}` with `{float}`
error[E0308]: mismatched types
== fails/h12b.rs
error: invalid comparison operator `===`
use std::num::ParseIntError;
struct Rect { width: u32, height: u32,}
fn area(r: &Rect) -> u32 { r.width * r.height}
trait Show { fn show(&self) -> String;}
impl Show for i32 { fn show(&self) -> String { format!("int {self}") }}
impl Show for &str { fn show(&self) -> String { format!("text {self}") }}
fn parse(s: &str) -> Result<i32, ParseIntError> { let n: i32 = s.parse()?; Ok(n)}
fn main() { // 1 x++ let mut x = 5; x += 1; println!("1 x += 1 -> {x}");
// 2 c ? a : b let big = true; let n = if big { 10 } else { 1 }; println!("2 if big {{ 10 }} else {{ 1 }} -> {n}");
// 3 for (i = 0; i < 3; i++) let mut seen = Vec::new(); for i in 0..3 { seen.push(i); } let stepped: Vec<i32> = (0..9).step_by(3).collect(); println!("3 0..3 -> {seen:?}, (0..9).step_by(3) -> {stepped:?}");
// 4 null let name: Option<String> = None; let shown = match &name { Some(s) => s.as_str(), None => "(none)", }; println!("4 Option<String> None -> {shown}");
// 5 implicit numeric conversion let small: i32 = 7; let wide: i64 = i64::from(small); let scaled = f64::from(small) * 1.5; println!("5 i64::from(7) -> {wide}, f64::from(7) * 1.5 -> {scaled}");
// 6 named arguments let r = Rect { width: 3, height: 4 }; println!("6 area(&Rect {{ width: 3, height: 4 }}) -> {}", area(&r));
// 7 overloading println!("7 1.show() -> {}, \"a\".show() -> {}", 1.show(), "a".show());
// 8 switch with fallthrough for n in [1, 2, 3, 9] { let word = match n { 1 => "one", 2 | 3 => "two or three", _ => "many", }; println!("8 match {n} -> {word}"); }
// 9 try / catch println!("9 parse(\"42\") -> {:?}", parse("42")); println!("9 parse(\"x\") -> {:?}", parse("x"));
// 10 truthiness let count = 3; let items: Vec<i32> = Vec::new(); println!("10 count != 0 -> {}, !items.is_empty() -> {}", count != 0, !items.is_empty());
// 11 string indexing let s = String::from("héllo"); println!( "11 chars().next() -> {:?}, as_bytes()[0] -> {}, &s[0..1] -> {:?}", s.chars().next(), s.as_bytes()[0], &s[0..1] );
// 12 == across types let k = 1; println!("12 k == 1 -> {}, f64::from(k) == 1.0 -> {}", k == 1, f64::from(k) == 1.0); println!("12 String == &str -> {}", String::from("a") == "a");}1 x += 1 -> 6
2 if big { 10 } else { 1 } -> 10
3 0..3 -> [0, 1, 2], (0..9).step_by(3) -> [0, 3, 6]
4 Option<String> None -> (none)
5 i64::from(7) -> 7, f64::from(7) * 1.5 -> 10.5
6 area(&Rect { width: 3, height: 4 }) -> 12
7 1.show() -> int 1, "a".show() -> text a
8 match 1 -> one
8 match 2 -> two or three
8 match 3 -> two or three
8 match 9 -> many
9 parse("42") -> Ok(42)
9 parse("x") -> Err(ParseIntError { kind: InvalidDigit })
10 count != 0 -> true, !items.is_empty() -> false
11 chars().next() -> Some('h'), as_bytes()[0] -> 104, &s[0..1] -> "h"
12 k == 1 -> true, f64::from(k) == 1.0 -> true
12 String == &str -> trueExpression or statement
Section titled “Expression or statement”- The last line of a block, with no
;, is its value. - A
;drops the value, so the block is worth(). if,matchandloopare expressions:let c = if a > 10 { "big" } else { "small" };.- Assignment and
+=are worth(). - A function body is a block, so no
returnis needed at the end.n * 2;there is E0308.
See Statements and expressions.
Runs: rules 1 to 5
fn tail(n: i32) -> i32 { n * 2}
fn main() { // 1 a block is worth its last line let a = { let w = 3; w * 4 }; // 2 a trailing ; makes it worth () let b = { let w = 3; let _ = w * 4; }; // 3 if, match and loop are expressions let c = if a > 10 { "big" } else { "small" }; let d = match a { 12 => "twelve", _ => "other", }; let mut k = 0; let e = loop { k += 1; if k == 3 { break k * 10; } }; // 4 assignment is worth () let mut m = 0; let f = m += 5; // 5 the function body is a block: no return needed println!("1 {a}"); println!("2 {b:?}"); println!("3 {c} {d} {e}"); println!("4 {f:?} {m}"); println!("5 {}", tail(4));}1 12 2 () 3 big twelve 30 4 () 5 5 8
Reading a signature
Section titled “Reading a signature”pub fn longest_by<'a, T, K>(items: &'a [T], key: impl Fn(&T) -> K) -> Option<&'a T>where K: Ord + Display,| Piece | Reads as |
|---|---|
pub fn longest_by |
a public function |
<'a, T, K> |
placeholders: a lifetime and two types, filled in by the call |
items: &'a [T] |
a borrowed slice of any T; the caller keeps the list |
key: impl Fn(&T) -> K |
any closure that takes &T and returns a K |
-> Option<&'a T> |
maybe one item, borrowed from items for 'a; None if the slice is empty |
where K: Ord + Display |
K must be orderable and printable |
More in Reading a signature.
Runs: this signature, called
use std::fmt::Display;
// pub fn name <generics> (parameters) -> result where boundspub fn longest_by<'a, T, K>(items: &'a [T], key: impl Fn(&T) -> K) -> Option<&'a T>where K: Ord + Display,{ let mut best: Option<&T> = None; for item in items { match best { Some(b) if key(b) >= key(item) => {} _ => best = Some(item), } } best}
fn main() { let words = ["fig", "banana", "kiwi"]; println!("{:?}", longest_by(&words, |w| w.len())); let none: [&str; 0] = []; println!("{:?}", longest_by(&none, |w| w.len()));}Some("banana")
NoneFormat strings
Section titled “Format strings”Left column: the format string. Right: what format! made of it, between bars. {} is for people, {:?} for programmers; {:#?} spreads over lines. A spec is {, a name or position, :, then fill, alignment (< ^ >), sign (+), #, 0, width, .precision and a type (?, x, b), then }; 1$ takes the width from argument 1. Syntax: Format strings; more in Formatting.
$ cargo run --bin fmt
{} |Ana|
{:?} |"Ana"|
{:?} |P { x: 1, y: 2 }|
{:>8} | Ana|
{:<8} |Ana |
{:^8} | Ana |
{:*^9} |***Ana***|
{:08.3} |0003.142|
{:.2} |3.14|
{:+} |+5|
{:5} | 42|
{:#x} |0xff|
{:#010b} |0b00000101|
{name} |Ana|
{0}-{0}-{1} |a-a-b|
{{}} |{}|
{:>1$} | Ana|
P {
x: 1,
y: 2,
}
Runs: the program
// Prints each format string next to what it produced, between |bars|.macro_rules! row { ($fmt:literal $(, $arg:expr)*) => { println!("{:<12} |{}|", $fmt, format!($fmt $(, $arg)*)) };}
#[derive(Debug)]#[allow(dead_code)]struct P { x: i32, y: i32,}
fn main() { let name = "Ana"; let pi = 3.14159; let p = P { x: 1, y: 2 }; row!("{}", name); row!("{:?}", name); row!("{:?}", p); row!("{:>8}", name); row!("{:<8}", name); row!("{:^8}", name); row!("{:*^9}", name); row!("{:08.3}", pi); row!("{:.2}", pi); row!("{:+}", 5); row!("{:5}", 42); row!("{:#x}", 255); row!("{:#010b}", 5); row!("{name}"); row!("{0}-{0}-{1}", "a", "b"); row!("{{}}"); row!("{:>1$}", name, 6); println!("{:#?}", p);}Naming
Section titled “Naming”| What | Style | Example |
|---|---|---|
| variables, functions, methods, modules, fields | snake_case |
user_count, parse_line |
| structs, enums, traits, variants | UpperCamelCase |
UserAccount, Shape::Circle |
| constants, statics | SCREAMING_SNAKE_CASE |
MAX_USERS |
| type parameters, lifetimes | short UpperCamelCase, often T; short lowercase |
T, 'a |
| method prefixes | as_ borrows, to_ makes a new value, into_ consumes |
as_str, to_uppercase, into_bytes |
The style rules are lints (non_snake_case and others); the prefixes come from the API Guidelines. See Naming.
Runs: every name above, with #![deny(nonstandard_style)]
#![deny(nonstandard_style)]
const MAX_USERS: u32 = 3;
struct UserAccount { user_count: u32,}
enum Shape { Circle(f64), Square(f64),}
trait Describe { fn describe(&self) -> String;}
impl Describe for Shape { fn describe(&self) -> String { match self { Shape::Circle(r) => format!("circle {r}"), Shape::Square(s) => format!("square {s}"), } }}
mod parse_line { pub fn first_word<'a, T: AsRef<str> + ?Sized>(s: &'a T) -> &'a str { s.as_ref().split(' ').next().unwrap_or("") }}
fn main() { let account = UserAccount { user_count: MAX_USERS }; println!("{} {}", account.user_count, parse_line::first_word("hi there")); println!("{} {}", Shape::Circle(1.5).describe(), Shape::Square(2.0).describe()); // method-name prefixes from the standard library let s = String::from("Hi"); let v = vec![1, 2]; println!("{} {} {}", s.as_str(), s.to_uppercase(), v.is_empty()); println!("{:?}", s.into_bytes());}3 hi circle 1.5 square 2 Hi HI false [72, 105]
Learn Rust is an independent project, not affiliated with the Rust Project or the Rust Foundation. Code and text are under the MIT licence.Source on GitHub.