Rust Language Cheat Sheet

Contains clickable links to The Book BK, Rust by Example EX, Std Docs STD, Nomicon NOM, Reference REF. Other symbols used: largely deprecated 🗑️, has a minimum edition '18, is work in progress 🚧, or bad 🛑.

Hello, Rust!

If you have never seen Rust before, or if you want to try the things below:

fn main() {
    println!("Hello, world!");

Data Structures

Data types and memory locations defined via keywords.

struct S {}Define a struct BK EX STD REF with named fields.
     struct S { x: T }Define struct with named field x of type T.
     struct S ​(T);Define "tupled" struct with numbered field .0 of type T.
     struct S;Define zero sized unit struct.
enum E {}Define an enum BK EX REF , c. algebraic data types, tagged unions.
     enum E { A, B(), C {} }Define variants of enum; can be unit- A, tuple- B ​() and struct-like C{}.
     enum E { A = 1 }If variants are only unit-like, allow discriminant values, e.g., for FFI.
union U {}Unsafe C-like union REF for FFI compatibility.
static X: T = T();Global variable BK EX REF with 'static lifetime, single memory location.
const X: T = T();Defines constant BK EX REF. Copied into a temporary when used.
let x: T;Allocate T bytes on stack 1 bound as x. Assignable once, not mutable.
let mut x: T;Like let, but allow for mutability and mutable borrow. 2
     x = y;Moves y to x, invalidating y if T is not Copy, and copying y otherwise.
1 They live on the stack for synchronous code. For async code these variables become part of the async's state machine which may ultimately reside on the heap.
2 Note that technically mutable and immutable are a bit of a misnomer. Even if you have an immutable binding or shared reference, it might contain a Cell, which supports so called interior mutability.

Creating and accessing data structures; and some more sigilic types.

S { x: y }Create struct S {} or use'ed enum E::S {} with field x set to y.
S { x }Same, but use local variable x for field x.
S { ..s }Fill remaining fields from s, esp. useful with Default.
S { 0: x }Like S ​(x) below, but set field .0 with struct syntax.
S​ (x)Create struct S ​(T) or use'ed enum E::S​ () with field .0 set to x.
SIf S is unit struct S; or use'ed enum E::S create value of S.
E::C { x: y }Create enum variant C. Other methods above also work.
()Empty tuple, both literal and type, aka unit STD
(x)Parenthesized expression.
(x,)Single-element tuple expression. EX STD REF
(S,)Single-element tuple type.
[S]Array type of unspecified length, i.e., slice. STD EX REF Can't live on stack. *
[S; n]Array type EX STD of fixed length n holding elements of type S.
[x; n]Array instance with n copies of x. REF
[x, y]Array instance with given elements x and y.
x[0]Collection indexing. Overloadable Index, IndexMut
x[..]Collection slice-like indexing via RangeFull, c. slices.
x[a..]Collection slice-like indexing via RangeFrom.
x[..b]Collection slice-like indexing RangeTo.
x[a..b]Collection slice-like indexing via Range.
a..bRight-exclusive range REF creation, also seen as ..b.
a..=bInclusive range creation, also seen as ..=b.
s.xNamed field access, REF might try to Deref if x not part of type S.
s.0Numbered field access, used for tuple types S ​(T).
* For now, see tracking issue and corresponding RFC 1909.

References & Pointers

Granting access to un-owned memory. Also see section on Generics & Constraints.

&SShared reference BK STD NOM REF (space for holding any &s).
     &[S]Special slice reference that contains (address, length).
     &strSpecial string reference that contains (address, length).
     &dyn SSpecial trait object BK reference that contains (address, vtable).
     &mut SExclusive reference to allow mutability (also &mut [S], &mut dyn S, ...)
*const SImmutable raw pointer type BK STD REF w/o memory safety.
*mut SMutable raw pointer type w/o memory safety.
&sShared borrow BK EX STD (e.g., address, len, vtable, ... of this s, like 0x1234).
&mut sExclusive borrow that allows mutability. EX
ref sBind by reference. EX 🗑️
*rDereference BK STD NOM a reference r to access what it points to.
     *r = s;If r is a mutable reference, move or copy s to target memory.
     s = *r;Make s a copy of whatever r references, if that is Copy.
     s = *my_box;Special case for Box that can also move out Box'ed content if it isn't Copy.
'aA lifetime parameter, BK EX NOM REF, duration of a flow in static analysis.
     &'a SOnly accepts a s with an address that lives 'a or longer.
     &'a mut SSame, but allow content of address to be changed.
     S<'a>Signals S will contain address with lifetime 'a. Creator of S decides 'a.
     fn f<'a>(t: &'a T)Same, for function. Caller decides 'a.
'staticSpecial lifetime lasting the entire program execution.

Functions & Behavior

Define units of code and their abstractions.

trait T {}Define a trait. BK EX REF
trait T : R {}T is subtrait of supertrait REF R. Any S must impl R before it can impl T.
impl S {}Implementation REF of functionality for a type S.
impl T for S {}Implement trait T for type S.
impl !T for S {}Disable an automatically derived auto trait NOM REF.
fn f() {}Definition of a function BK EX REF; or associated function if inside impl.
     fn f() -> S {}Same, returning a value of type S.
     fn f(&self) {}Define a method as part of an impl.
const fn f() {}Constant fn usable at compile time, e.g., const X: u32 = f(Y). '18
async fn f() {}Async '18 function transformation, makes f return an impl Future. STD
     async fn f() -> S {}Same, but make f return an impl Future<Output=S>.
     async { x }Used within a function, make { x } an impl Future<Output=X>.
fn() -> SFunction pointers, BK STD REF don't confuse with trait Fn.
|| {} A closure BK EX REF that borrows its captures.
     |x| {}Closure with a bound parameter x.
     |x| x + xClosure without block expression.
     move |x| x + y Closure taking ownership of its captures.
     return || true Closures may sometimes look like logical ORs (here: return a closure).
unsafe {}If you need to crash your code in production; unsafe code. BK EX NOM REF

Control Flow

Control execution within a function.

while x {}Loop REF, run while expression x is true.
loop {}Loop infinitely REF until break. Can yield value with break x.
for x in iter {}Syntactic sugar to loop over iterators. BK STD REF
if x {} else {}Conditional branch REF if expression is true.
'label: loop {}Loop label EX REF, useful for flow control in nested loops.
breakBreak expression REF to exit a loop.
     break xSame, but make x value of the loop expression (only in actual loop).
     break 'labelExit not only this loop, but the enclosing one marked with 'label.
continue Continue expression REF to the next loop iteration of this loop.
continue 'labelSame, but instead of enclosing loop marked with 'label.
x?If x is Err or None, return and propagate. BK EX STD REF
x.awaitOnly works inside async. Yield flow until Future or Stream ? x ready. '18
return xEarly return from function. More idiomatic way is to end with expression.
f()Invoke callable f (e.g., a function, closure, function pointer, Fn, ...).
x.f()Call member function, requires f takes self, &self, ... as first argument.
     X::f(x)Same as x.f(). Unless impl Copy for X {}, f can only be called once.
     X::f(&x)Same as x.f().
     X::f(&mut x)Same as x.f().
     S::f(&x)Same as x.f() if X derefs to S, i.e., x.f() finds methods of S.
     T::f(&x)Same as x.f() if X impl T, i.e., x.f() finds methods of T if in scope.
X::f()Call associated function, e.g., X::new().
     <X as T>::f()Call trait method T::f() implemented for X.

Organizing Code

Segment projects into smaller units and minimize dependencies.

mod m {}Define a module BK EX REF, get definition from inside {}.
mod m;Define a module, get definition from or m/
a::bNamespace path EX REF to element b within a (mod, enum, ...).
     ::bSearch b relative to crate root. 🗑️
     crate::bSearch b relative to crate root. '18
     self::bSearch b relative to current module.
     super::bSearch b relative to parent module.
use a::b;Use EX REF b directly in this scope without requiring a anymore.
use a::{b, c};Same, but bring b and c into scope.
use a::b as x;Bring b into scope but name x, like use std::error::Error as E.
use a::b as _;Bring b anonymously into scope, useful for traits with conflicting names.
use a::*;Bring everything from a into scope.
pub use a::b;Bring a::b into scope and reexport from here.
pub T"Public if parent path is public" visibility BK for T.
     pub(crate) TVisible at most in current crate.
     pub(self) TVisible at most in current module.
     pub(super) TVisible at most in parent.
     pub(in a::b) TVisible at most in a::b.
extern crate a;Declare dependency on external crate BK EX REF 🗑️ ; just use a::b in '18.
extern "C" {}Declare external dependencies and ABI (e.g., "C") from FFI. BK EX NOM REF
extern "C" fn f() {}Define function to be exported with ABI (e.g., "C") to FFI.

Type Aliases and Casts

Short-hand names of types, and methods to convert one type to another.

type T = S;Create a type alias BK REF, i.e., another name for S.
SelfType alias for implementing type REF, e.g. fn new() -> Self.
selfMethod subject in fn f(self) {}, same as fn f(self: Self) {}.
     &selfSame, but refers to self as borrowed, same as f(self: &Self)
     &mut selfSame, but mutably borrowed, same as f(self: &mut Self)
     self: Box<Self>Arbitrary self type, add methods to smart pointers (my_box.f_of_self()).
S as TDisambiguate BK REF type S as trait T, e.g., <X as T>::f().
S as RIn use of symbol, import S as R, e.g., use a::b as x.
x as u32Primitive cast EX REF, may truncate and be a bit surprising. NOM

Macros & Attributes

Code generation constructs expanded before the actual compilation happens.

m!()Macro BK STD REF invocation, also m!{}, m![] (depending on macro).
$x:tyMacro capture, also $x:expr, $x:ty, $x:path, ... see next table.
$xMacro substitution in macros by example. BK EX REF
$(x),*Macro repetition "zero or more times" in macros by example.
     $(x),?Same, but "zero or one time".
     $(x),+Same, but "one or more times".
     $(x)<<+In fact separators other than , are also accepted. Here: <<.
$crateSpecial hygiene variable, crate where macros is defined. ?
#[attr]Outer attribute. EX REF, annotating the following item.
#![attr]Inner attribute, annotating the surrounding item.

In a macro_rules! implementation, the following macro captures can be used:

Macro CaptureExplanation
$x:itemAn item, like a function, struct, module, etc.
$x:blockA block {} of statements or expressions, e.g., { let x = 5; }
$x:stmtA statement, e.g., let x = 1 + 1;, String::new(); or vec![];
$x:exprAn expression, e.g., x, 1 + 1, String::new() or vec![]
$x:patA pattern, e.g., Some(t), (17, 'a') or _.
$x:tyA type, e.g., String, usize or Vec<u8>.
$x:identAn identifier, for example in let x = 0; the identifier is x.
$x:pathA path (e.g. foo, ::std::mem::replace, transmute::<_, int>, …).
$x:literalA literal (e.g. 3, "foo", b"bar", etc.).
$x:metaA meta item; the things that go inside #[...] and #![...] attributes.
$x:ttA single token tree, see here for more details.

Pattern Matching

Constructs found in match or let expressions, or function parameters.

match m {}Initiate pattern matching BK EX REF, then use match arms, c. next table.
let S(x) = get();Notably, let also pattern matches similar to the table below.
     let S { x } = s;Only x will be bound to value s.x.
     let (_, b, _) = abc;Only b will be bound to value abc.1.
     let (a, ..) = abc;Ignoring 'the rest' also works.
     let Some(x) = get();Won't work 🛑 if pattern can be refuted REF, use if let instead.
if let Some(x) = get() {}Branch if pattern can actually be assigned (e.g., enum variant).
fn f(S { x }: S)Function parameters also work like let, here x bound to s.x of f(s).

Pattern matching arms in match expressions. The left side of these arms can also be found in let expressions.

E::A => {}Match enum variant A, c. pattern matching. BK EX REF
E::B ( .. ) => {}Match enum tuple variant B, wildcard any index.
E::C { .. } => {}Match enum struct variant C, wildcard any field.
S { x: 0, y: 1 } => {}Match struct with specific params.
S { x, y } => {}Match struct with any values, bind respective fields as variables x and y.
S { .. } => {}Match struct with any values.
D => {}Match enum variant E::D if D in use.
D => {}Match anything, bind D; possibly false friend 🛑 of E::D if D not in use.
_ => {}Proper wildcard that matches anything / "all the rest".
[a, 0] => {}Match array with any value for a and 0 for second.
(a, 0) => {}Match tuple with any value for a and 0 for second.
x @ 1..=5 => {}Bind matched to x; pattern binding. BK EX
0 | 1 => {}Pattern alternatives (or-patterns).
     E::A | E::Z Same, but on enum variants.
     E::C {x} | E::D {x}Same, but bind x if all variants have it.
S { x } if x > 10Pattern match guards. BK EX

Generics & Constraints

Generics combine with many other constructs such as struct S<T>, fn f<T>(), ...

S<T>A generic BK EX type with a type parameter (T is placeholder name here).
S<T: R>Type short hand trait bound BK EX specification (R must be actual trait).
     T: R, P: SIndependent trait bounds (here one for T and one for P).
     T: R, SCompile error 🛑, you probably want compound bound R + S below.
     T: R + SCompound trait bound BK EX, T must fulfill R and S.
     T: R + 'aSame, but w. lifetime. T must fulfill R, if T has lifetimes, must outlive 'a.
     T: ?SizedOpt out of a pre-defined trait bound, here Sized. ?
     T: 'aType lifetime bound EX; if T has references, they must outlive 'a.
     'b: 'aLifetime 'b must live at least as long as (i.e., outlive) 'a bound.
S<T> where T: RSame as S<T: R> but more pleasant to read for longer bounds.
S<T = R>Default type parameter BK for associated type.
S<'_>Inferred anonymous lifetime.
S<_>Inferred anonymous type, e.g., as let x: Vec<_> = iter.collect()
S::<T>Turbofish STD call site type disambiguation, e.g. f::<u32>().
trait T<X> {}A trait generic over X. Can have multiple impl T for S (one per X).
trait T { type X; }Defines associated type BK REF X. Only one impl T for S possible.
     type X = R;Set associated type within impl T for S { type X = R; }.
impl<T> S<T> {}Implement functionality for any T in S<T>.
impl S<T> {}Implement functionality for exactly S<T> (e.g., S<u32>).
fn f() -> impl TExistential types BK, returns an unknown-to-caller S that impl T.
fn f(x: &impl T)Trait bound,"impl traits" BK, somewhat similar to fn f<S:T>(x: &S).
fn f(x: &dyn T)Marker for dynamic dispatch BK REF, f will not be monomorphized.
fn f() where Self: RIn a trait T {}, mark f as accessible only on types that also impl R.
for<'a>Higher-rank trait bounds. NOM REF

Strings & Chars

Rust has several ways to create string or char literals, depending on your needs.

"..."String literal, REF UTF-8, will escape \n to line break 0xA, ...
r"...",Raw string literal. REF UTF-8, won't escape \n, ...
r#"..."#, etc.Raw string literal, UTF-8, but can also contain ".
b"..."Byte string literal; REF constructs ASCII [u8], not a string.
br"...", br#"..."#, etc.Raw byte string literal, ASCII [u8], combination of the above.
'🦀'Character literal, REF fixed 4 byte unicode 'char'. STD
b'x'ASCII byte literal. REF


No comment.

//Line comment, use these to document code flow or internals.
//!Inner line doc comment BK EX REF for auto generated documentation.
///Outer line doc comment, use these on types.
/*...*/Block comment.
/*!...*/Inner block doc comment.
/**...*/Outer block doc comment.
```rust ... ```In doc comments, include a doc test (doc code running on cargo test).
#In doc tests, hide line from documentation (``` # use x::hidden; ```).


These sigils did not fit any other category but are good to know nonetheless.

!Always empty never type. 🚧 BK EX STD REF
_Unnamed variable binding, e.g., |x, _| {}.
_xVariable binding explicitly marked as unused.
1_234_567Numeric separator for visual clarity.
1_u8Type specifier for numeric literals EX REF (also i8, u16, ...).
0xBEEF, 0o777, 0b1001Hexadecimal (0x), octal (0o) and binary (0b) integer literals.
r#fooA raw identifier BK EX for edition compatibility.
x;Statement REF terminator, c. expressions EX REF

Common Operators

Rust supports all common operators you would expect to find in a language (+, *, %, =, ==...). Since they behave no differently in Rust we do not list them here. For some of them Rust also supports operator overloading. STD

Data & Types

Memory representations of common data types.

Basic Types

Memory representations are depicted for little-endian architectures (e.g., x86-64).

Integer Types REF

u8, i8 u16, i16 u32, i32 u64, i64 u128, i128 usize, isize Same as ptr on platform.
Integer*Max Value
* i8, i16, ... values range from -max/2 to max/2, rounded towards negative infinity.

Float Types REF

f32 f64

Float types are slightly more complicated and follow IEEE 754-2008.


Textual Types REF

char Any UTF-8 char. str ... U T F - 8 ... unspecified times Rarely seen alone, but as &str instead.

Notice how:

  • char is always 4 bytes and only holds a single Unicode scalar value (thus possibly wasting space),
  • str is a byte-array of unknown length guaranteed to hold UTF-8 code points (but harder to index).

Custom Types

Basic types that can be defined by the user. Actual layout REF is subject to representation. REF Additional padding can be present.

T: Sized T T: ?Sized T Dynamically
Sized Types REF
(A, B, C) A B C struct S; struct S { b: B, c: C } B C [T; n] T T T ... n times [T] ... T T T ... unspecified times

These sum types hold a value of one of their sub types:

enum E { A, B, C } Tag A exclusive or Tag B exclusive or Tag C Safely holds A or B or C, also
called 'tagged union', though
compiler may omit tag.
union { ... } A unsafe or B unsafe or C Can unsafely reinterpret
memory. Result might
be undefined.

References & Pointers

References give safe access to another memory location. As can be seen below, lifetimes are not encoded at runtime. Pointers give unsafe access to other memory.

&'a T ptr4/8 | T During 'a any 'mem'
this targets must always
be a valid t of T.
&'a mut T ptr4/8 | T Same, but location
'mem' may not be
*const T ptr4/8 No guarantees. *mut T ptr4/8 No guarantees.

Rust also has a number of special reference types that encode more than just an address, see below. The respective &mut version is identical and omitted:

&'a [T] ptr4/8 len4/8 | ... T T ... &'a str ptr4/8 len4/8 | ... U T F - 8 ... &'a dyn Trait ptr4/8 ptr4/8 | T |
*Drop::drop(&mut T)
*Trait::f(&T, ...)
*Trait::g(&T, ...)
Where *Drop::drop(), *Trait::f(), ... are pointers to their respective impl for T.

Box is Rust's type for heap allocation that also comes in a number of special variants:

Box<T> ptr4/8 | T Box<[T]> ptr4/8 len4/8 | T T T ... Box<dyn Trait> ptr4/8 ptr4/8 | T | ...
Compare above.

Standard Library Types

Rust's standard library combines many of the above primitive types into useful types with special semantics. Some common types:

UnsafeCell<T> T Magic type allowing
aliased mutability.
Cell<T> T Allows T's
to move in
and out.
RefCell<T> borrowed T Also support dynamic
borrowing of T. Like Cell this
is Send, but not Sync.
AtomicUsize usize4/8 Other atomic similarly. Option<T> Tag or Tag T Tag may be omitted for
certain T.
Result<T, E> Tag E or Tag T

These dynamic collections grow when needed and are backed by the heap:

Vec<T> ptr4/8 capacity4/8 len4/8 |
T T ... len
String ptr4/8 capacity4/8 len4/8 |
U T F - 8 ... len
Observe how String differs from &str and &[char].

Shared ownership of memory and resources. If the type does not contain a Cell for T, these are often combined with one of the Cell types above to allow shared de-facto mutability.

Rc<T> ptr4/8
| strong4/8 weak4/8 T
Share ownership of T in same thread. Needs nested Cell
or RefCellto allow mutation. Is neither Send nor Sync.
Arc<T> ptr4/8
| strong4/8 weak4/8 T
Same, but allow sharing between threads IF contained
T itself is Send and Sync.
Mutex<T> / RwLock<T> ptr4/8 poisoned4/8 T | lock Needs to be held in Arc to be shared between
threads, always Send and Sync. Consider using
parking_lot instead (faster, no heap usage).


Project Anatomy

Basic project layout, and common files and folders, as used by Rust tooling.

benches/Benchmarks for your crate, run via cargo bench, only useful in nightly. 🚧
examples/Examples how to use your crate, run via cargo run --example my_example.
src/Actual source code for your project.
     build.rsPre-build script, e.g., when compiling C / FFI, needs to be specified in Cargo.toml.
     main.rsDefault entry point for applications, this is what cargo run uses.
     lib.rsDefault entry point for libraries. This is where lookup for my_crate::f starts.
tests/Integration tests go here, invoked via cargo test. Unit tests often stay in src/ file.
.rustfmt.tomlIn case you want to customize how cargo fmt works.
.clippy.tomlSpecial configuration for certain clippy lints.
Cargo.tomlMain project configuration. Defines dependencies, artifacts ...
Cargo.lockDependency details for reproducible builds, recommended to git for apps, not for libs.

An minimal library entry point with functions and modules looks like this:

// src/ (default library entry point)

pub fn f() {}      // Is a public item in root, so it's accessible from the outside.

mod m {
    pub fn g() {}  // No public path (`m` not public) from root, so `g`
}                  // is not accessible from the outside of the crate.

For binaries (not depicted), function fn main() {} is the entry point. Unit tests (not depicted), usually reside in a #[cfg(test)] mod test { } next to their code. Integration tests and benchmarks, in their basic, form look like this:

// tests/ (sample integration test)

fn my_sample() {
    assert_eq!(my_crate::f(), 123); // Integration tests (and benchmarks) 'depend' to the crate like
}                                   // a 3rd party would. Hence, they only see public items.
// benches/ (sample benchmark)

#![feature(test)]   // #[bench] is still experimental

extern crate test;  // Even in '18 this is needed ... for reasons.
                    // Normally you don't need this in '18 code.

use test::{black_box, Bencher};

fn my_algo(b: &mut Bencher) {
    b.iter(|| black_box(my_crate::f())); // `black_box` prevents `f` from being optimized away.

Idiomatic Rust

If you are used to programming Java or C, consider these.

Think in Expressionsx = if x { a } else { b };
x = loop { break 5 };
fn f() -> u32 { 0 }
Think in Iterators(1..10).map(f).collect()
names.iter().filter(|x| x.starts_with("A"))
Handle Absence with ?x = try_something()?;
Use Strong Typesenum E { Invalid, Valid { ... } } over ERROR_INVALID = -1
enum E { Visible, Hidden } over visible: bool
struct Charge(f32) over f32
Provide BuildersCar::new("Model T").hp(20).run();
Split ImplementationsGeneric types S<T> can have a separate impl per T.
Rust doesn't have OO, but with separate impl you can get specialization.
UnsafeAvoid unsafe {}, often safer, faster solution without it. Exception: FFI.
Implement Traits#[derive(Debug, Copy, ...)] and custom impl where needed.
ToolingWith clippy you can improve your code quality.
Formatting with rustfmt helps others to read your code.
Add unit tests BK (#[test]) to ensure your code works.
Add doc tests BK (``` my_api::f() ```) to ensure docs match code.
DocumentationAnnotate your APIs with doc comments that can show up on
Don't forget to include a summary sentence and the Examples heading.
If applicable: Panics, Errors, Safety, Abort and Undefined Behavior.

🔥 We highly recommend you also follow the API Guidelines (Checklist) for any shared project! 🔥


Async-Await 101

If you are familiar with async / await in C# or TypeScript, here are some things to keep in mind:

asyncAnything declared async always returns an impl Future<Output=_>. STD
     async fn f() {}Function f returns an impl Future<Output=()>.
     async fn f() -> S {}Function f returns an impl Future<Output=S>.
     async { x }Transforms { x } into an impl Future<Output=X>.
let sm = f(); Calling f() that is async will not execute f, but produce state machine sm. 1
     sm = async { g() };Likewise, does not execute the { g() } block; produces state machine.
runtime.block_on(sm); 2Outside an async {}, schedules sm to actually run. Would execute g().
sm.awaitInside an async {}, run sm until complete. Yield to runtime if sm not ready.
1 Technically async transforms the following code into an anonymous, compiler-generated state machine type, and f() instantiates that machine. The state machine always impl Future, possibly Send & co, depending on types you used inside async. State machine driven by worker thread invoking Future::poll() via runtime directly, or parent .await indirectly.
2 Right now Rust doesn't come with its own runtime. Use external crate instead, such as async-std or tokio 0.2+. Also, Futures in Rust are an MPV. There is much more utility stuff in the futures crate.

At each x.await, state machine passes control to subordinate state machine x. At some point a low-level state machine invoked via .await might not be ready. In that the case worker thread returns all the way up to runtime so it can drive another Future. Some time later the runtime:

  • might resume execution. It usually does, unless sm / Future dropped.
  • might resume with the previous worker or another worker thread (depends on runtime).

Simplified diagram for code written inside an async block :

       consecutive_code();           consecutive_code();           consecutive_code();
START --------------------> x.await --------------------> y.await --------------------> READY
// ^                          ^     ^                               Future<Output=X> ready -^
// Invoked via runtime        |     |
// or an external .await      |     This might resume on another thread (next best available),
//                            |     or NOT AT ALL if Future was dropped.
//                            |
//                            Execute `x`. If ready: just continue execution; if not, return
//                            this thread to runtime.

This leads to the following considerations when writing code inside an async construct:

Constructs 1Explanation
sleep_or_block();Definitely bad 🛑, never halt current thread, clogs executor.
set_TL(a); x.await; TL();Definitely bad 🛑, await may return from other thread, thread local invalid.; x.await; s.go();Maybe bad 🛑, await will not return if Future dropped while waiting. 2
Rc::new(); x.await; rc();Non-Send types prevent impl Future from being Send; less compatible.
1 Here we assume s is any non-local that could temporarily be put into an invalid state; TL is any thread local storage, and that the async {} containing the code is written without assuming executor specifics.
2 Since Drop is run in any case when Future is dropped, consider using drop guard that cleans up / fixes application state if it has to be left in bad condition across .await points.


There is a subtrait relationship Fn : FnMut : FnOnce. That means, a closure that implements Fn, also implements FnMut and FnOnce. Likewise, a closure that implements FnMut, also implements FnOnce.

From a call site perspective that means:

SignatureFunction g can call ...Function g accepts ...
g<F: FnOnce()>(f: F)... f() once.Fn, FnMut, FnOnce
g<F: FnMut()>(mut f: F)... f() multiple times.Fn, FnMut
g<F: Fn()>(f: F)... f() multiple times.Fn
Notice how asking for a Fn closure as a function is most restrictive for the caller; but having a Fn closure as a caller is most compatible with any function.

From the perspective of someone defining a closure:

|| { moved_s; } FnOnceCaller must give up ownership of moved_s.
|| { &mut s; } FnOnce, FnMutAllows g() to change caller's local state s.
|| { &s; } FnOnce, FnMut, FnMay not mutate state; but can share and reuse s.
* Rust prefers capturing by reference (resulting in the most "compatible" Fn closures from a caller perspective), but can be forced to capture its environment by copy or move via the move || {} syntax.

That gives the following advantages and disadvantages:

F: FnOnceEasy to satisfy as caller.Single use only, g() may call f() just once.
F: FnMutAllows g() to change caller state.Caller may not reuse captures during g().
F: FnMany can exist at same time.Hardest to produce for caller.

A Guide to Reading Lifetimes

Lifetimes can be overwhelming at times. Here is a simplified guide on how to read and interpret constructs containing lifetimes if you are familiar with C.

ConstructHow to read
let s: S = S(0)A location that is S-sized, named s, and contains the value S(0).
If declared with let, that location lives on the stack. 1
Generally, s can mean location of s, and value within s.
As a location, s = S(1) means, assign value S(1) to location s.
As a value, f(s) means call f with value inside of s.
To explicitly talk about its location (address) we do &s.
To explicitly talk about a location that can hold such a location we do &S.
&'a SA &S is a location that can hold (at least) an address, called reference.
Any address stored in here must be that of a valid S.
Any address stored must be proven to exist for at least (outlive) duration 'a.
In other words, the &S part sets bounds for what any address here must contain.
While the &'a part sets bounds for how long any such address must at least live.
The lifetime our containing location is unrelated, but naturally always shorter.
Duration of 'a is purely compile time view, based on static analysis.
&SSometimes 'a might be elided (or can't be specified) but it still exists.
Within methods bodies, lifetimes are determined automatically.
Within signatures, lifetimes may be 'elided' (annotated automatically).
&sThis will produce the actual address of location s, called 'borrow'.
The moment &s is produced, location s is put into a borrowed state.
Checking if in borrowed state is based on compile-time analysis.
This analysis is based on all possible address propagation paths.
As long as any &s could be around, s cannot be altered directly.
For example, in let a = &s; let b = a;, also b needs to go.
Borrowing of s stops once last &s is last used, not when &s dropped.
&mut sSame, but will produce a mutable borrow.
A &mut will allow the owner of the borrow (address) to change s content.
This reiterates that not the value in s, but location of s is borrowed.
1 Compare Data Structures section above: while true for synchronous code, an async 'stack frame' might actually be placed on to the heap by the used async runtime.

When reading function or type signatures in particular:

ConstructHow to read
S<'a> {}Signals that S will contain* at least one address (i.e., reference).
'a will be determined automatically by the user of this struct.
'a will be chosen as small as possible.
f<'a>(x: &'a T)Signals this function will accept an address (i.e., reference).
                    -> &'a S... and that it returns one.
'a will be determined automatically by the caller.
'a will be chosen as small as possible.
'a will be picked so that it satisfies input and output at call site.
More importantly, propagate borrow state according to lifetime names!
So while result address with 'a is used, input address with 'a is locked.
Here: while s from let s = f(&x) is around, x counts as 'borrowed'.
<'a, 'b: 'a>The lifetimes declared in S<> and f<> can also have bounds.
The <'a, 'b> part means the type will handle at least 2 addresses.
The 'b: 'a part is a lifetime bound, and means 'b must outlive 'a.
Any address in an &'b X must exist at least as long as any in an &'a Y.
* Technically the struct may not hold any data (e.g., when using the 'a only for PhantomData or function pointers) but still make use of the 'a for communicating and requiring that some of its functions require reference of a certain lifetime.

Invisible Sugar

If something works that "shouldn't work now that you think about it", it might be due to one of these.

Coercions NOM'Weaken' types to match signature, e.g., &mut T to &T.
Deref NOMDeref x: T until *x, **x, ... compatible with some target S.
Prelude STDAutomatic import of basic types.
ReborrowSince x: &mut T can't be copied; move new &mut *x instead.
Lifetime Elision BK NOM REFAutomatically annotate f(x: &T) to f<'a>(x: &'a T).
Method Resolution REFDeref or borrow x until x.f() works.

Unsafe, Unsound, Undefined

Unsafe leads to unsound. Unsound leads to undefined. Undefined leads to the dark side of the force.

Unsafe Code

  • Code marked unsafe has special permissions, e.g., to deref raw pointers, or invoke other unsafe functions.
  • Along come special promises the author must uphold to the compiler, and the compiler will trustingly reason and optimize based on these promises.
  • By itself unsafe code is not bad, but dangerous, and needed for FFI, assembly, or some exotic data structures.
// `x` must always point to race-free, valid, aligned, initialized u8 memory.
unsafe fn unsafe_f(x: *mut u8) {

Undefined Behavior (UB)

  • As mentioned, unsafe code implies special promises to the compiler (it wouldn't need be unsafe otherwise).
  • Failure to uphold any promise makes the compiler produce wrong code, giving an undefined program.
  • With undefined behavior anything is possible. Insidiously, the effects may be 1) subtle, 2) manifest far away from the site of violation or 3) be visible only under certain conditions.
  • A seemingly working program (incl. any number of unit tests) is no proof UB code might not fail on a whim.
  • Code with UB is objectively dangerous and invalid and should never exist.
if user_pressed_x() {                                  // Even if user never pressed `x` compiler
    let r: &mut u8 = unsafe { &mut *ptr::null_mut() }; // might have reasoned backwards b/c broken
}                                                      // promise and tainted whole app.

Unsound Code

  • Any safe Rust that could (even only theoretically) produce undefined behavior is unsound.
  • Such safe Rust code should instead be tagged unsafe with all critical contracts documented.
  • Any unsafe Rust code that for its user unavoidably (and undocumented) exhibits UB is likewise unsound.
  • Unsound code is of particular concern in libraries, where its UB'ness depends on how the code is used.
  • As such, unsound code exposed through a library is a liability to users as it violates basic assumption many Rust users have, while unsound code within an application are accidents waiting to happen.
fn unsound_ref<T>(x: &T) -> &u128 {      // Signature looks safe to users. Happens to be
    unsafe { mem::transmute(x) }         // ok if invoked with an &u128, UB for practically
}                                        // everything else.

Responsible use of Unsafe

  • Do not use unsafe unless you absolutely have to.
  • Follow the Nomicon, Unsafe Guidelines, always uphold all safety invariants, and never invoke UB.
  • Minimize the use of unsafe and encapsulate it in a small, boring units that are trivial to review.
  • Each unsafe invocation should be accompanied by plain-text comments explaining why it is safe.

Formatting Strings

Formatting applies to print!, eprint!, write! (and their -ln siblings like println!). The format! macro can create a formatted String.

Each format argument follows a basic grammar:

{[argument][':'[[fill]align][sign]['#']['0'][width]['.' precision][type]]}

The full grammar is specified in the std::fmt documentation, but here are some commonly used flags:

argumentOmitted (next {}), number (0, 1, ...) or identifier for named arguments.
alignLeft (<), center (^), or right (>) , if width is specified, fills with fill.
#Alternate formatting. Pretty-print with {:#?}, for example.
0Zero-pads numeric values.
widthMinimum width (≥ 0), padding with fill (default to space).
precisionDecimal digits (≥ 0) for numerics, or max width for non-numerics.
typeDebug (?), hex (x), binary (b), or octal (o) (there are more, using Traits).
Note that width and precision can use other arguments as their values, allowing for dynamic sizing of fields.
{:#?}Pretty-print the next argument using Debug.
{2:#?}Pretty-print the 3rd argument using Debug.
{val:^2$}Center the val named argument, width specified by the 3rd argument.
{:<10.3}Left align with width 10 and a precision of 3.
{val:#x}Format val argument as hex, with a leading 0x (alternate format for x).


Some commands and tools that are good to know.

cargo initCreate a new project for the latest edition.
cargo buildBuild the project in debug mode (--release for all optimization).
cargo checkCheck if project would compile (much faster).
cargo testRun tests for the project.
cargo runRun your project, if a binary is produced (
cargo doc --openLocally generate documentation for your code and dependencies.
cargo rustc -- -Zunpretty=XShow more desugared Rust code, in particular with X being:
     expandedShow with expanded macros, ...
cargo +{nightly, stable} ...Runs command with given toolchain, e.g., for 'nightly only' tools.
rustup docsOpen offline Rust documentation (incl. the books), good on a plane!
A command like cargo build means you can either type cargo build or just cargo b.

These are optional rustup components. Install them with rustup component add [tool].

cargo clippyAdditional (lints) catching common API misuses and unidiomatic code. 🔗
cargo fmtAutomatic code formatter (rustup component add rustfmt). 🔗

A large number of additional cargo plugins can be found here.


These are other great visual guides and tables.

A preview image.
A preview image.
Macro Railroad
A preview image.
Cheat SheetsDescription
Rust Learning⭐Probably the best collection of links about learning Rust.
Functional Jargon in RustA collection of functional programming jargon explained in Rust.
String ConversionsHow to get type of string from another.
Periodic Table of TypesHow various types and references correlate.
FuturesHow to construct and work with futures.
Rust Iterator Cheat SheetSummary of iterator-related methods from std::iter and itertools.
Type-Based Rust Cheat SheetLists common types and how they convert.

All major Rust books developed by the community.

Books ️📚Description
The Rust Programming LanguageStandard introduction to Rust, start here if you are new.
     The API GuidelinesHow to write idiomatic and re-usable Rust.
     The Async Book 🚧Explains async code, Futures, ...
     The Edition GuideWorking with Rust 2015, Rust 2018, and beyond.
     The Little Book of Rust Macros 🚧Community's collective knowledge of Rust macros.
     The Reference 🚧Reference of the Rust language.
     The RFC Book Look up accepted RFCs and how they change the language.
     The Rust CookbookCollection of simple examples that demonstrate good practices.
     The Rustc GuideExplains how the compiler works internally.
     The Rustdoc BookTips how to customize cargo doc and rustdoc.
     The RustonomiconDark Arts of Advanced and Unsafe Rust Programming.
     The SIMD Performance Guide 🚧Work with u8x32 or f32x8 to speed up your computations.
     The Unsafe Code Guidelines 🚧Concise information about writing unsafe code.
     The Unstable BookInformation about unstable items, e.g, #![feature(...)].
The Cargo BookHow to use cargo and write Cargo.toml.
The CLI BookInformation about creating CLI tools.
The Embedded BookWorking with embedded and #![no_std] devices.
     The EmbedonomiconFirst #![no_std] from scratch on a Cortex-M.
The WebAssembly BookWorking with the web and producing .wasm files.
     The wasm-bindgen GuideHow to bind Rust and JavaScript APIs in particular.

Comprehensive lookup tables for common components.

Tables 📋Description
Compiler Error IndexEver wondered what E0404 means?
ALL the Clippy LintsAll the clippy lints you might be interested in.
Configuring RustfmtAll rustfmt options you can use in .rustfmt.toml.
Rust ChangelogSee all the things that changed in a particular version.
Rust ForgeLists release train and links for people working on the compiler.
     Rust Platform SupportAll supported platforms and their Tier.
     Rust Component HistoryCheck nightly status of various Rust tools for a platform.

Online services which provide information or tooling.

Services ⚙️Description
crates.ioAll 3rd party libraries for Rust.
std.rsShortcut to std documentation.
docs.rsDocumentation for 3rd party libraries, automatically generated from source.
lib.rsUnofficial overview of quality Rust libraries and applications.
Rust PlaygroundTry and share snippets of Rust code.

Printing & PDF

Want this Rust cheat sheet as a PDF download? Generate PDF (or select File > Print – might take 10s so) and then "Save as PDF". It looks great in both Firefox's and Chrome's PDF exports. Alternatively use the cached PDF.

Fork me on GitHub