Rust Cheat Sheet
Rust Cheat Sheet
Contains clickable links to The Book BK, Rust by Example EX, Std Docs STD, Nomicon NOM, Reference REF.
Data Structures
Data types and memory locations defined via keywords.
Example Explanation
struct S {} Define a struct BK EX STD REF with named fields.
struct S; Define zero sized NOM unit struct. Occupies no space, optimized away.
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, REF e.g., for FFI.
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 mut x: T; Like let , but allow for mutability BK EX and mutable borrow.2
Creating and accessing data structures; and some more sigilic types.
Example Explanation
S { x: y } Create struct S {} or use 'ed enum E:"S {} with field x set to y .
S { .$s } Fill remaining fields from s , esp. useful with Default:"default() . STD
https://cheats.rs/#_print Page 1 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
S (x) Create struct S (T) or use 'ed enum E:"S () with field .0 set to x .
[S] Array type of unspecified length, i.e., slice. EX STD REF Can't live on stack. *
[S; n] Array type EX STD REF of fixed length n holding elements of type S .
x[.$] Same, via range (here full range), also x[a.$b] , x[a..%b] , … c. below.
s.x Named field access, REF might try to Deref if x not part of type S .
Example Explanation
&S Shared reference BK STD NOM REF (type; space for holding any &s ).
&mut S Exclusive reference to allow mutability (also &mut [S] , &mut dyn S , …).
&s Shared borrow BK EX STD (e.g., address, len, vtable, … of this s , like 0x1234 ).
*const S Immutable raw pointer type BK STD REF w/o memory safety.
&raw const s Create raw pointer w/o going through reference; c. ptr:addr_of!() STD !
!
https://cheats.rs/#_print Page 2 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
&raw mut s Same, but mutable. Raw ptrs. are needed for unaligned, packed fields. !
let S { ref mut x } = s; Mutable ref binding ( let x = &mut s.x ), shorthand destructuring ↓ version.
s = *r; Won't work if *r is not Copy , as that would move and leave empty place.
s = *my_box; Special case for Box STD that can also move out b'ed content that isn't Copy .
struct S<'a> {} Signals this S will contain address with lifetime 'a . Creator of S decides 'a .
trait T<'a> {} Signals any S , which impl T for S , might contain address.
fn f<'a>(t: &'a T) Signals this function handles some address. Caller decides 'a .
Example Explanation
trait T {} Define a trait; BK EX REF common behavior types can adhere to.
trait T : R {} T is subtrait of supertrait BK EX REF R . Any S must impl R before it can impl T .
impl T for S {} Implement trait T for type S ; specifies how exactly S acts like T .
struct S (T); More arcanely, also↑ defines fn S(x: T) -( S constructor function. RFC !
const fn f() {} Constant fn usable at compile time, e.g., const X: u32 = f(Y) . '18
async fn f() {} Async REF '18 function transformation, ↓ makes f return an impl Future . STD
Fn() -( S Callable Trait BK STD (also FnMut , FnOnce ), implemented by closures, fn's …
|| {} A closure BK EX REF that borrows its captures, ↓ REF (e.g., a local variable).
https://cheats.rs/#_print Page 3 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
|x| x + x Same, without block expression; may only consist of single expression.
move |x| x + y Move closure REF taking ownership; i.e., y transferred into closure.
return |* true Closures sometimes look like logical ORs (here: return a closure).
unsafe If you enjoy debugging segfaults Friday night; unsafe code. ↓ BK EX NOM REF
unsafe fn f() {} Means "calling can cause UB, ↓ YOU must check requirements".
unsafe trait T {} Means "careless impl. of T can cause UB; implementor must check".
unsafe { f(); } Guarantees to compiler "I have checked requirements, trust me".
unsafe impl T for S {} Guarantees S is well-behaved w.r.t T ; people may use T on S safely.
1 Most documentation calls them function pointers, but function references might be more appropriate as they can't be null and must point to valid target.
Control Flow
Control execution within a function.
Example Explanation
while x {} Loop, REF
run while expression x is true.
loop {} Loop indefinitely REF until break . Can yield value with break x .
'label: {} Block label, RFC can be used with break to exit out of this block. 1.65+
'label: loop {} Similar loop label, EX REF useful for flow control in nested loops.
break 'label x Break out of block or loop named 'label and make x its value.
continue Continue expression REF to the next loop iteration of this loop.
continue 'label Same but instead of this loop, enclosing loop marked with 'label.
x? If x is Err or None, return and propagate. BK EX STD REF
x.await Syntactic sugar to get future, poll, yield. REF '18 Only works inside async .
x.into_future() Effectively converts any IntoFuture STD type into proper future first.
future.poll() On proper Future STD then poll() and yield flow if Poll:"Pending . STD
return x Early return REF from function. More idiomatic is to end with expression.
|| { return } Within closures return exits that closure only, i.e., closure is s. function.
async { return } Inside async a return only REF exits that {} , i.e., async {} is s. function.
x.f() Call member function, requires f takes self , &self , … as first argument.
https://cheats.rs/#_print Page 4 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
X:"f(x) Same as x.f() . Unless impl Copy for X {} , f can only be called once.
Organizing Code
Segment projects into smaller units and minimize dependencies.
Example Explanation
mod m {} Define a module, BK EX REF get definition from inside {} . ↓
:"b Search b in crate root '15 REF or external prelude; '18 REF global path. REF
use a:"b; Use EX REF b directly in this scope without requiring a anymore.
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 in, only recommended if a is some prelude. STD
pub use a:"b; Bring a:"b into scope and reexport from here.
extern crate a; Declare dependency on external crate; BK 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.
1 Items in child modules always have access to any item, regardless if pub or not.
https://cheats.rs/#_print Page 5 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Example Explanation
type T = S; Create a type alias, BK REF i.e., another name for S .
Self Type alias for implementing type, REF e.g., fn new() -( Self .
&self Same, but refers to self as borrowed, would equal f(self: &Self)
&mut self Same, but mutably borrowed, would equal f(self: &mut Self)
self: Box<Self> Arbitrary self type, add methods to smart pointers ( my_box.f_of_self() ).
x as u32 Primitive cast, EX REF may truncate and be a bit surprising. 1 NOM
1 See Type Conversions below for all the ways to convert between types.
Example Explanation
m!() Macro BK STD REF
invocation, also m!{} , m![] (depending on macro).
$(x)</+ In fact separators other than , are also accepted. Here: </ .
1 Applies to 'macros by example'. REF
2 See Tooling Directives below for all captures.
Pattern Matching
Constructs found in match or let expressions, or function parameters.
Example Explanation
match m {} Initiate pattern matching, BK EX REF then use match arms, c. next table.
let S(x) = get(); Notably, let also destructures EX similar to the table below.
let s @ S { x } = get(); Bind s to S while x is bound to s.x , pattern binding, BK EX REF c. below !
let (|x| x) = get(); Pathological or-pattern,↓ not closure. Same as let x = get(); !
let Some(x) = get(); Won't work if pattern can be refuted, REF use let else or if let instead.
let Some(x) = get() else {}; Assign if possible,RFC if not else {} w. must break , return , panic! , … 1.65+
if let Some(x) = get() {} Branch if pattern can be assigned (e.g., enum variant), syntactic sugar. *
while let Some(x) = get() {} Equiv.; here keep calling get() , run {} as long as pattern can be assigned.
fn f(S { x }: S) Function parameters also work like let , here x bound to s.x of f(s) . !
* Desugars to match get() { Some(x) =1 {}, _ =1 () } .
Pattern matching arms in match expressions. Left side of these arms can also be found in let expressions.
S { x: 0, y: 1 } =1 {} Match struct with specific values (only accepts s with s.x of 0 and s.y of 1 ).
S { x: a, y: b } =1 {} Match struct with any values and bind s.x to a and s.y to b .
S { x, y } =1 {} Same, but shorthand with s.x and s.y bound as x and y respectively.
E:"C {x} | E:"D {x} =1 {} Same, but bind x if all variants have it.
(a, 0) =1 {} Match tuple with any value for a and 0 for second.
[a, 0] =1 {} Slice pattern, REF match array with any value for a and 0 for second.
[1, .$] =1 {} Match array starting with 1 , any value for rest; subslice pattern. REF RFC
[1, x @ .$, 5] =1 {} Same, but also bind x to slice representing middle (c. pattern binding).
[a, x @ .$, b] =1 {} Same, but match any first, last, bound as a , b respectively.
https://cheats.rs/#_print Page 7 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Err(x @ Error {.$}) =1 {} Also works nested, here x binds to Error , esp. useful with if below.
S { x } if x > 10 =1 {} Pattern match guards, BK EX REF condition must be true as well to match.
Example Explanation
struct S<T> … A generic BK EX type with a type parameter ( T is placeholder name here).
S<T> where T: R Trait bound, BK EX REF limits allowed T , guarantees T has R ; R must be trait.
where T: R, P: S Independent trait bounds, here one for T and one for (not shown) P .
where T: R + 'a Same, but w. lifetime. T must fulfill R , if T has lifetimes, must outlive 'a .
where T: 'a Type lifetime bound; EX if T has references, they must outlive 'a .
where T: 'static Same; does esp. not mean value t will live 'static , only that it could.
where 'b: 'a Lifetime 'b must live at least as long as (i.e., outlive) 'a bound.
where u8: R<T> Also allows you to make conditional statements involving other types. !
S<T: R> Short hand bound, almost same as above, shorter to write.
S<const N: usize> Generic const bound; REF user of type S can provide constant value N .
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 RFC X . Only one impl T for S possible.
trait T { type X<G>; } Defines generic associated type (GAT), RFC e.g., X can be generic Vec<> . 1.65+
type X<G> = R<G>; Same for GAT, e.g., impl T for S { type X<G> = Vec<G>; } .
impl<T> S<T> {} Implement fn 's for any T in S<T> generically, REF here T type parameter.
impl S<T> {} Implement fn 's for exactly S<T> inherently, REF here T specific type, e.g., u8 .
fn f(x: &impl T) Trait bound via "impl traits", BK somewhat like fn f<S: T>(x: &S) below.
https://cheats.rs/#_print Page 8 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
fn f(x: &dyn T) Invoke f via dynamic dispatch, BK REF f will not be instantiated for x .
fn f() where Self: R; In trait T {} , make f accessible only on types known to also impl R .
fn f() where Self: Sized; Using Sized can opt f out of dyn T trait object vtable, enabling trait obj.
fn f() where Self: R {} Other R useful w. dflt. methods (non dflt. would need be impl'ed anyway).
Higher-Ranked Items !
Actual types and traits, abstract over something, usually lifetimes.
Example Explanation
for<'a> Marker for higher-ranked bounds. NOM REF !
trait T: for<'a> R<'a> {} Any S that impl T would also have to fulfill R for any lifetime.
fn(&'a u8) Function pointer type holding fn callable with specific lifetime 'a .
for<'a> fn(&'a u8) Higher-ranked type1 holding fn callable with any lt.; subtype↓ of above.
fn(&'_ u8) Same; automatically expanded to type for<'a> fn(&'a u8) .
dyn for<'a> Fn(&'a u8) Higher-ranked (trait-object) type, works like fn above.
dyn Fn(&'_ u8) Same; automatically expanded to type dyn for<'a> Fn(&'a u8) .
dyn Fn(&u8) Same; automatically expanded to type dyn for<'a> Fn(&'a u8) .
1 Yes, the for<> is part of the type, which is why you write impl T for for<'a> fn(&'a u8) below.
impl T for for<'a> fn(&'a u8) {} For fn. pointer, where call accepts any lt., impl trait T .
Example Explanation
"..2" String literal, REF, 1 UTF-8, will interpret the following escapes, …
r"..2" Raw string literal. REF, 1UTF-8, but won't interpret any escape above.
r#"..2"# Raw string literal, UTF-8, but can also contain " . Number of # can vary.
b"..2" Byte string literal; REF, 1 constructs ASCII [u8] , not a string.
br"..2" , br#"..2"# Raw byte string literal, ASCII [u8] , combination of the above.
REF STD
https://cheats.rs/#_print Page 9 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
' ' Character literal, REF fixed 4 byte unicode 'char'. STD
Documentation
Debuggers hate him. Avoid bugs with this one weird trick.
Example Explanation
//4 Outer line doc comment,1 BK EX REF use these on types, traits, functions, …
/5! Inner line doc comment, mostly used at start of file to document module.
/6 … *8 Block comment. 2
Miscellaneous
These sigils did not fit any other category but are good to know nonetheless.
Example Explanation
! Always empty never type. BK EX STD REF
fn f() -( ! {} Function that never returns; compat. with any type e.g., let x: u8 = f();
fn f() -( Result<(), !> {} Function that must return Result but signals it can never Err .
fn f(x: !) {} Function that exists, but can never be called. Not very useful. !
_ Unnamed wildcard REF variable binding, e.g., |x, _| {} .
let _ = x; Unnamed assignment is no-op, does not move out x or preserve scope!
1_u8 Type specifier for numeric literals EX REF (also i8 , u16 , …).
Common Operators
Rust supports most operators you would expect ( + , * , % , = , =: , …), including overloading. STD Since they behave no differently in Rust
we do not list them here.
https://cheats.rs/#_print Page 10 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Overview
With rare exceptions you are never 'allowed to reason' about the actual CPU. You write code for an
abstracted CPU. Rust then (sort of) understands what you want, and translates that into actual RISC-V /
x86 / … machine code.
is not a runtime, and does not have any runtime overhead, but is a computing model abstraction,
contains concepts such as memory regions (stack, …), execution semantics, …
knows and sees things your CPU might not care about,
is de-facto a contract between you and the compiler,
and exploits all of the above for optimizations.
Misconceptions
On the left things people may incorrectly assume they should get away with if Rust targeted CPU directly.
On the right things you'd interfere with if in reality if you violate the AM contract.
Without AM With AM
0xffff_ffff would make a valid char . AM may exploit 'invalid' bit patterns to pack unrelated
data.
AM pointers can have 'domain' attached for
0xff and 0xff are same pointer.
optimization.
AM may issue cache-friendly ops trusting 'no read can
Any r/w on pointer 0xff always fine.
happen'.
https://cheats.rs/#_print Page 11 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Reading un-init just gives random value. AM 'knows' read impossible, may remove all related
bitcode.
Data race just gives random value. AM may split R/W, produce impossible value, see above.
Null reference is just 0x0 in some
Holding 0x0 in reference summons Cthulhu.
register.
This table is only to outline what the AM does. Unlike C or C++, Rust never lets you do the wrong thing
unless you force it with unsafe . ↓
Language Sugar
If something works that "shouldn't work now that you think about it", it might be due to one of these.
Name Description
Coercions NOM Weakens types to match signature, e.g., &mut T to &T ; c. type conversions. ↓
Lifetime Elision BK NOM REF Allows you to write f(x: &T) , instead of f<'a>(x: &'a T) , for brevity.
Match Ergonomics RFC Repeatedly dereferences scrutinee and adds ref and ref mut to bindings.
Rvalue Static Promotion RFC ! Makes references to constants 'static , e.g., &42 , &None , &mut [] .
Dual Definitions RFC ! Defining one thing (e.g., struct S(u8) ) implicitly def. another (e.g., fn S ).
Opinion — These features make your life easier using Rust, but stand in the way of learning it. If you want to develop a genuine
understanding, spend some extra time exploring them.
Application Memory
https://cheats.rs/#_print Page 12 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
1 For fixed-size values stack is trivially managable: take a few bytes more while you need them, discarded once you leave.
However, giving out pointers to these transient locations form the very essence of why lifetimes exist; and are the subject of
the rest of this chapter.
S(1)
t Variables
let t = S(1);
Reserves memory location with name t of type S and the value S(1) stored inside.
If declared with let that location lives on stack. 1
Note the linguistic ambiguity,2 in the term variable, it can mean the:
1. name of the location in the source file ("rename that variable"),
2. location in a compiled app, 0x7 ("tell me the address of that variable"),
3. value contained within, S(1) ("increment that variable").
Specifically towards the compiler t can mean location of t , here 0x7 , and value within t , here
S(1) .
1
Compare above,↑ true for fully synchronous code, but async stack frame might placed it on heap via runtime.
S(1)
a t Moves
let a = t;
https://cheats.rs/#_print Page 13 of 88
… }
Rust Language Cheat Sheet 7/22/23, 12:55 PM
… }
M {
c Type Safety
let c: S = M:"new();
)
S(3
S(1) S(2)
▼ ▼
t Scope & Drop
{
let mut c = S(2);
c = S(3); /5 <- Drop called on `c` before assignment.
let t = S(1);
let a = t;
} /5 <- Scope of `a`, `t`, `c` ends here, drop called on `a`, `c`.
Once the 'name' of a non-vacated variable goes out of (drop-)scope, the contained value is
dropped.
Rule of thumb: execution reaches point where name of variable leaves {} -block it was
defined in
In detail more tricky, esp. temporaries, …
Drop also invoked when new value assigned to existing variable location.
In that case Drop:"drop() is called on the location of that value.
In the example above drop() is called on a , twice on c , but not on t .
Most non- Copy values get dropped most of the time; exceptions include mem:"forget() , Rc cycles,
abort() .
https://cheats.rs/#_print Page 14 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Call Stack
S(1)
a x Function Boundaries
fn f(x: S) { … }
When a function is called, memory for parameters (and return values) are reserved on stack.1
Here before f is invoked value in a is moved to 'agreed upon' location on stack, and during f
works like 'local variable' x .
1
Actual location depends on calling convention, might practically not end up on stack at all, but that doesn't change mental
model.
S(1)
a x x Nested Functions
fn f(x: S) {
if once() { f(x) } /5 <- We are here (before recursion)
}
let a = S(1);
f(a);
Recursively calling functions, or calling other functions, likewise extends the stack frame.
Nesting too many invocations (esp. via unbounded recursion) will cause stack to grow, and
eventually to overflow, terminating the app.
S(1) M { }
a x m Repurposing Memory
https://cheats.rs/#_print Page 15 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
fn f(x: S) {
if once() { f(x) }
let m = M:"new() /5 <- We are here (after recursion)
}
let a = S(1);
f(a);
Stack that previously held a certain type will be repurposed across (even within) functions.
Here, recursing on f produced second x , which after recursion was partially reused for m .
Key take away so far, there are multiple ways how memory locations that previously held a valid value
of a certain type stopped doing so in the meantime. As we will see shortly, this has implications for
pointers.
S(1) 0x3
a r References as Pointers
let a = S(1);
let r: &S = &a;
A reference type such as &S or &mut S can hold the location of some s .
Here type &S , bound as name r , holds location of variable a ( 0x3 ), that must be type S , obtained
via &a .
If you think of variable c as specific location, reference r is a switchboard for locations.
The type of the reference, like all other types, can often be inferred, so we might omit it from now
on:
https://cheats.rs/#_print Page 16 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
References can read from ( &S ) and also write to ( &mut S ) location they point to.
The dereference *r means to neither use the location of or value within r , but the location r points
to.
In example above, clone d is created from *r , and S(2) written to *r .
Method Clone:"clone(&T) expects a reference itself, which is why we can use r , not *r .
On assignment *r = … old value in location also dropped (not shown above).
M { x }
▼
0x3
let mut a = …;
let r = &mut a;
let d = *r; /5 Invalid to move out value, `a` would be empty.
*r = M:"new(); /5 invalid to store non S value, doesn't make sense.
While bindings guarantee to always hold valid data, references guarantee to always point to valid
data.
Esp. &mut T must provide same guarantees as variables, and some more as they can't dissolve the
target:
They do not allow writing invalid data.
They do not allow moving out data (would leave target empty w/o owner knowing).
0x3
p Raw Pointers
https://cheats.rs/#_print Page 17 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Lifetime Basics
"Lifetime" of Things
Every entity in a program has some (temporal / spatial) room where it is relevant, i.e., alive.
Loosely speaking, this alive time can be1
1. the LOC (lines of code) where an item is available (e.g., a module name).
2. the LOC between when a location is initialized with a value, and when the location is
abandoned.
3. the LOC between when a location is first used in a certain way, and when that usage
stops.
4. the LOC (or actual time) between when a value is created, and when that value is dropped.
Within the rest of this section, we will refer to the items above as the:
1. scope of that item, irrelevant here.
2. scope of that variable or location.
3. lifetime2 of that usage.
4. lifetime of that value, might be useful when discussing open file descriptors, but also
irrelevant here.
Likewise, lifetime parameters in code, e.g., r: &'a S , are
concerned with LOC any location r points to needs to be accessible or locked;
unrelated to the 'existence time' (as LOC) of r itself (well, it needs to exist shorter, that's it).
&'static S means address must be valid during all lines of code.
1There is sometimes ambiguity in the docs differentiating the various scopes and lifetimes. We try to
be pragmatic here, but suggestions are welcome.
2
Live lines might have been a more appropriate term …
S(2) 0xa
c r Meaning of r: &'c S
https://cheats.rs/#_print Page 18 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
a b c r Typelikeness of Lifetimes
{
let b = S(3);
{
let c = S(2);
let r: &'c S = &c; /5 Does not quite work since we can't name lifetimes of local
{ /5 variables in a function body, but very same principle
applies
let a = S(0); /5 to functions next page.
)
S(4
▼
0x6
b Borrowed State
print_byte(r);
Once the address of a variable is taken via &b or &mut b the variable is marked as borrowed.
While borrowed, the content of the address cannot be modified anymore via original binding b .
Once address taken via &b or &mut b stops being used (in terms of LOC) original binding b works
again.
https://cheats.rs/#_print Page 19 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Lifetimes in Functions
b c r x Function
y Parameters
let b = S(1);
let c = S(2);
When calling functions that take and return references two interesting things happen:
The used local variables are placed in a borrowed state,
But it is during compilation unknown which address will be returned.
S(1) S(2) ?
let b = S(1);
let c = S(2);
print_byte(r);
Since f can return only one address, not in all cases b and c need to stay locked.
In many cases we can get quality-of-life improvements.
Notably, when we know one parameter couldn't have been used in return value anymore.
S(1) S(2) y + _
https://cheats.rs/#_print Page 20 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
let b = S(1);
let c = S(2);
let r = f(&b, &c); /5 We know returned reference is `c`-based, which must stay locked,
/5 while `b` is free to move.
let a = b;
print_byte(r);
S(2)
a c Unlocking
let r = f(&c);
let s = r;
/5 <- Not here, `s` prolongs locking of `c`.
print_byte(s);
A variable location is unlocked again once the last use of any reference that may point to it ends.
https://cheats.rs/#_print Page 21 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Advanced !
!
▼ ▼
https://cheats.rs/#_print Page 22 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
/5 Some fictitious place where we use `ra` and `rval`, both alive.
compute(ra, rval);
Here ( M ) means compilation fails because mutability error, ( L ) lifetime error. Also, dereference *rb not strictly necessary, just
added for clarity.
https://cheats.rs/#_print Page 23 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
f_sr cases always work, short reference (only living 'b ) can always be produced.
f_sm cases usually fail simply because mutable chain to S needed to return &mut S .
f_lr cases can fail because returning &'a S from &'a mut S to caller means there would now exist
two references (one mutable) to same S which is illegal.
f_lm cases always fail for combination of reasons above.
S(1)
▼
_ Drop and _
{
let f = |x, y| (S(x), S(y)); /5 Function returning two 'Droppables'.
println!("…");
}
Here Scope means contained value lives until end of scope, i.e., past the println!() .
Memory Layout
Byte representations of common types.
Basic Types
Essential types built into the core of the language.
https://cheats.rs/#_print Page 24 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
usize , isize
Unsigned Types
u16 65_535
u32 4_294_967_295
u64 18_446_744_073_709_551_615
u128 340_282_366_920_938_463_463_374_607_431_768_211_455
Signed Types
i16 32_767
i32 2_147_483_647
i64 9_223_372_036_854_775_807
i128 170_141_183_460_469_231_731_687_303_715_884_105_727
https://cheats.rs/#_print Page 25 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
i8 -128
i16 -32_768
i32 -2_147_483_648
i64 -9_223_372_036_854_775_808
i128 -170_141_183_460_469_231_731_687_303_715_884_105_728
Float Types!
S E E E E E E E E F F F F F F F F F F F F F F F F F F F F F F F
Explanation:
Casting Pitfalls
https://cheats.rs/#_print Page 26 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Arithmetic Pitfalls
STD
f32:"NAN =: f32:"NAN false Use f32:"is_nan() instead.
1
Expression _100 means anything that might contain the value 100 , e.g., 100_i32 , but is opaque to compiler.
d Debug build.
r Release build.
char str
… U T F - 8 … unspecified times
https://cheats.rs/#_print Page 27 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Basics
Type Description
char Always 4 bytes and only holds a single Unicode scalar value .
str An u8 -array of unknown length guaranteed to hold UTF-8 encoded code points.
Usage
Chars Description
let c = 'a'; Often a char (unicode scalar) can coincide with your intuition of character.
let c = '❤'; It can also hold many Unicode symbols.
But not always. Given emoji is two char (see Encoding) and can't be held by c
let c = ' ';
.1
c =
Also, chars are not allowed to hold arbitrary bit patterns.
0xffff_ffff;
1
Fun fact, due to the Zero-width joiner (⨝) what the user perceives as a character can get even more unpredictable: is in fact
5 chars ⨝ ⨝ , and rendering engines are free to either show them fused as one, or separately as three, depending on
their abilities.
Strings Description
let s = "a"; A str is usually never held directly, but as &str , like s here.
let s = "❤ "; It can hold arbitrary text, has variable length per c., and is hard to index.
Encoding!
49 00 00 00 20 00 00 00 64 27 00 00
s.chars() 1
20 00 00 00 52 00 00 00 75 00 00 00 73 00 …
t.as_bytes() 49 20 e2 9d a4 ef b8 8f 20 52 75 73 74 4
49 00 00 00 20 00 00 00 64 27 00 00 0f fe 01 00
t.chars() 1
20 00 00 00 52 00 00 00 75 00 …
https://cheats.rs/#_print Page 28 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
For what seem to be browser bugs Safari and Edge render the hearts in Footnote 3 and 4 wrong, despite being able to
differentiate them correctly in s and t above.
Custom Types
Basic types definable by users. Actual layout REF is subject to representation; REF padding can be present.
Sized ↓ Maybe DST ↓ Fixed array of n elements. Slice type of unknown-many elements. Neither
Sized (nor carries len information), and most
A B C B C
Zero-Sized ↓ or maybe or maybe
B A C C ↦ B
Also note, two types A(X, Y) and B(X, Y) with exactly the same fields can still have differing layout; never transmute() STD without representation guarantees.
https://cheats.rs/#_print Page 29 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
enum E { A, B, C } union { … }
Tag A A
exclusive or unsafe or
Tag B B
exclusive or unsafe or
Tag C C
&'a T *const T
Pointer Meta
Many reference and pointer types can carry an extra field, pointer metadata. STD It can be the element- or byte-length of the target, or
a pointer to a vtable. Pointers with meta are called fat, otherwise thin.
ptr 2/4/8 ptr 2/4/8 len 2/4/8 ptr 2/4/8 len 2/4/8
| | |
T ←T→ … T T …
(any mem) (any mem) (any mem)
No meta for If T is a DST struct such as Regular slice reference (i.e., the
sized target. S { x: [u8] } meta field len is reference type of a slice type [T] ) ↑
(pointer is thin). count of dyn. sized content. often seen as &[T] if 'a elided.
https://cheats.rs/#_print Page 30 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
&'a str
… U T F - 8 …
(any mem)
←T→ *Drop:"drop(&mut T)
(any mem)
size
align
*Trait:"f(&T, …)
*Trait:"g(&T, …)
(static vtable)
Closures
Ad-hoc functions with an automatically managed data block capturing REF, 1 environment where closure was defined. For example, if
you had:
let y = ..2;
let z = ..2;
with_closure(move |x| x + y.f() + z); /5 y and z are moved into closure instance (of type C1)
with_closure( |x| x + y.f() + z); /5 y and z are pointed at from closure instance (of type C2)
Then the generated, anonymous closures types C1 and C2 passed to with_closure() would look like:
https://cheats.rs/#_print Page 31 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Y Z
(any mem) (any mem)
Also produces anonymous fn such as fc1(C1, X) or fc2(&C2, X) . Details depend on which FnOnce , FnMut , Fn ... is supported, based on properties of captured
types.
1 A bit oversimplified a closure is a convenient-to-write 'mini function' that accepts parameters but also needs some local variables to do its job. It is therefore a type
(containing the needed locals) and a function. 'Capturing the environment' is a fancy way of saying that and how the closure type holds on to these locals, either by
moved value, or by pointer. See Closures in APIs ↓ for various implications.
Tag T Tag T T
Tag may be omitted for Either some error E or value Uninitialized memory or
certain T, e.g., NonNull . of T . some T . Only legal way
to work with uninit data.
Order-Preserving Collections
https://cheats.rs/#_print Page 32 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Box<T> Vec<T>
ptr 2/4/8 meta 2/4/8 ptr 2/4/8 capacity 2/4/8 len 2/4/8
| |
←T→ T T … len
For some T stack proxy may carry Regular growable array vector of single type.
meta↑ (e.g., Box<[T]> ).
LinkedList<T> ! VecDeque<T>
head 2/4/8 tail 2/4/8 len 2/4/8 head 2/4/8 len 2/4/8 ptr 2/4/8 capacity 2/4/8
| | |
Elements head and tail both null or point to nodes on Index head selects in array-as-ringbuffer. This means content may be
the heap. Each node can point to its prev and next node. non-contiguous and empty in the middle, as exemplified above.
Eats your cache (just look at the thing!); don't use unless
you evidently must.
Other Collections
bmask 2/4/8 ctrl 2/4/8 left 2/4/8 len 2/4/8 ptr 2/4/8 capacity 2/4/8 len 2/4/8
| |
Stores keys and values on heap according to hash value, SwissTable Heap stored as array with 2N elements per layer. Each T
implementation via hashbrown. HashSet identical to HashMap , can have 2 children in layer below. Each T larger than its
just type V disappears. Heap view grossly oversimplified. children.
Owned Strings
https://cheats.rs/#_print Page 33 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
ptr 2/4/8 capacity 2/4/8 len 2/4/8 ptr 2/4/8 len 2/4/8 Platform Defined
| | |
U T F - 8 … len A B C … len … ∅ /
← capacity → (heap) (heap) (heap)
Observe how String differs from &str and &[char] . NUL-terminated but w/o NUL in middle. Encapsulates how operating system
represents strings (e.g., WTF-8 on
Windows).
PathBuf
OsString
|
/
(heap)
Shared Ownership
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> Arc<T>
strng 2/4/8 weak 2/4/8 ←T→ strng 2/4/8 weak 2/4/8 ←T→
(heap) (heap)
Share ownership of T in same thread. Needs nested Cell Same, but allow sharing between threads IF contained
or RefCell to allow mutation. Is neither Send nor Sync . T itself is Send and Sync .
Mutex<T> / RwLock<T>
Standard Library
https://cheats.rs/#_print Page 34 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
One-Liners
Snippets that are common, but still easy to forget. See Rust Cookbook for more.
Strings
Intent Snippet
Concatenate strings (any Display ↓ that is). 1 '21 format!("{x}{y}")
1 Allocates; if x or y are not going to be used afterwards consider using write! or std:"ops:"Add .
2 Requires regex crate.
I/O
Intent Snippet
Create a new file File:"create(PATH)?
Same, via
OpenOptions:"new().create(true).write(true).truncate(true).open(PATH)?
OpenOptions
Macros
Intent Snippet
macro_rules! var_args { ($($args:expr),*) =1 {{ }}
Macro w. variable arguments
}
https://cheats.rs/#_print Page 35 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Transforms
Esoterics!
Intent Snippet
wants_closure({ let c = outer.clone(); move ||
Cleaner closure captures
use_clone(c) })
Fix inference in ' try ' closures iter.try_for_each(|x| { Ok:"<(), Error>(()) })?;
Thread Safety
Assume you hold some variables in Thread 1, and want to either move them to Thread 2, or pass their references to Thread 3.
Whether this is allowed is governed by Send STD and Sync STD respectively:
| | | | Thread 2
| | | |
Mutex<u32> Cell<u32> MutexGuard<u32> Rc<u32>
|
&Mutex<u32> |
&Cell<u32> |
&MutexGuard<u32> |&Rc<u32> Thread 3
Example Explanation
Mutex<u32> Both Send and Sync . You can safely pass or lend it to another thread.
Cell<u32> Send , not Sync . Movable, but its reference would allow concurrent non-atomic writes.
https://cheats.rs/#_print Page 36 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
MutexGuard<u32> Sync , but not Send . Lock tied to thread, but reference use could not allow data race.
1 If is Sync .
T
2 If is Send .
T
3 If you need to send a raw pointer, create newtype struct Ptr(*const u8) and unsafe impl Send for Ptr {} . Just ensure you may send it.
Iterators
Processing elements in a collection.
Basics
Style Description
Imperative, useful w. side effects, interdepend., or need to break flow
for x in c { ..2 }
early.
c.iter().map().filter()
Functional, often much cleaner when only results of interest.
..2
STD
c_iter.next() Low-level, via explicit Iterator:"next() invocation. !
c.get(n) Manual, bypassing official iteration machinery.
Opinion — Functional style is often easiest to follow, but don't hesitate to use for if your .iter()
chain turns messy. When implementing containers iterator support would be ideal, but when in a hurry
it can sometimes be more practical to just implement .len() and .get() and move on with your life.
Obtaining
Basics
c.into_iter() 1 — Turns collection c into an Iterator STD i and consumes2 c . Standard way to get
iterator.
c.iter() — Courtesy method some collections provide, returns borrowing Iterator, doesn't
https://cheats.rs/#_print Page 37 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
consume c .
c.iter_mut() — Same, but mutably borrowing Iterator that allow collection to be changed.
The Iterator
For Loops
1 Requires IntoIterator STD for C to be implemented. Type of item depends on what C was.
2 If it looks as if it doesn't consume c that's because type was Copy . For example, if you call (&c).into_iter() it will invoke
.into_iter() on &c (which will consume a copy of the reference and turn it into an Iterator), but the original c remains
untouched.
Creating
Essentials
Let's assume you have a struct Collection<T> {} you authored. You should also implement:
struct IntoIter<T> {} — Create a struct to hold your iteration status (e.g., an index) for value
iteration.
impl Iterator for IntoIter<T> {} — Implement Iterator:"next() so it can produce elements.
Collection<T> IntoIter<T>
⌾ Iterator
Item = T;
At this point you have something that can behave as an Iterator, STD but no way of actually obtaining it.
See the next tab for how that usually works.
For Loops
Many users would expect your collection to just work in for loops. You need to implement:
https://cheats.rs/#_print Page 38 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
As you can see, the IntoIterator STD trait is what actually connects your collection with the IntoIter trait
you created in the previous tab.
Borrowing
In addition, if you want your collection to be useful when borrowed you should implement:
Collection:"iter(&self) -( Iter ,
Collection:"iter_mut(&mut self) -( IterMut .
Iter<T> IterMut<T>
⌾ Iterator ⌾ Iterator
Item = &T; Item = &mut T;
Interoperability
Iterator Interoperability
https://cheats.rs/#_print Page 39 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
STD
In addition, also consider adding the extra traits from std:"iter to your iterators:
Writing collections can be work. The good news is, if you followed all steps in this section your
collection will feel like a first class citizen.
Number Conversions
As-correct-as-it-currently-gets number conversions.
1
If type true subset from() works directly, e.g., u32:"from(my_u8) .
2
Truncating ( 11.9_f32 as u8 gives 11 ) and saturating ( 1024_f32 as u8 gives 255 ); c. below.
3 Might misrepresent number ( u64:"MAX as f32 ) or produce Inf ( u128:"MAX as f32 ).
Also see Casting- and Arithmetic Pitfalls ↑ for more things that can go wrong working with numbers.
String Conversions
If you want a string of type …
String
CString x.into_string()?
OsString x.to_str()?.to_string()
https://cheats.rs/#_print Page 40 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
PathBuf x.to_str()?.to_string()
Vec<u8> 1 String:"from_utf8(x)?
i
&str x.to_string()
&CStr x.to_str()?.to_string()
&OsStr x.to_str()?.to_string()
&Path x.to_str()?.to_string()
&[u8] 1 String:"from_utf8_lossy(x).to_string()
CString
CString x
OsString 2 CString:"new(x.to_str()?)?
PathBuf CString:"new(x.to_str()?)?
Vec<u8> 1 CString:"new(x)?
&str CString:"new(x)?
i
&CStr x.to_owned()
&OsStr 2 CString:"new(x.to_os_string().into_string()?)?
&Path CString:"new(x.to_str()?)?
&[u8] 1 CString:"new(Vec:"from(x))?
OsString
CString OsString:"from(x.to_str()?)
OsString x
PathBuf x.into_os_string()
Vec<u8> 1 ?
i
&str OsString:"from(x)
https://cheats.rs/#_print Page 41 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
&CStr OsString:"from(x.to_str()?)
i
&OsStr OsString:"from(x)
&Path x.as_os_str().to_owned()
&[u8] 1 ?
PathBuf
CString PathBuf:"from(x.to_str()?)
i
OsString PathBuf:"from(x)
PathBuf x
Vec<u8> 1 ?
i
&str PathBuf:"from(x)
&CStr PathBuf:"from(x.to_str()?)
i
&OsStr PathBuf:"from(x)
i
&Path PathBuf:"from(x)
&[u8] 1 ?
Vec<u8>
CString x.into_bytes()
OsString ?
PathBuf ?
Vec<u8> 1 x
&str Vec:"from(x.as_bytes())
&CStr Vec:"from(x.to_bytes_with_nul())
&OsStr ?
&Path ?
&[u8] 1 x.to_vec()
https://cheats.rs/#_print Page 42 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
&str
CString x.to_str()?
OsString x.to_str()?
PathBuf x.to_str()?
Vec<u8> 1 std:"str:"from_utf8(&x)?
&str x
&CStr x.to_str()?
&OsStr x.to_str()?
&Path x.to_str()?
&[u8] 1 std:"str:"from_utf8(x)?
&CStr
CString x.as_c_str()
OsString 2 x.to_str()?
PathBuf ?,4
&str ?,4
&CStr x
&OsStr 2 ?
&Path ?
&OsStr
https://cheats.rs/#_print Page 43 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
String OsStr:"new(&x)
CString ?
OsString x.as_os_str()
PathBuf x.as_os_str()
Vec<u8> 1 ?
&str OsStr:"new(x)
&CStr ?
&OsStr x
&Path x.as_os_str()
&[u8] 1 ?
&Path
CString Path:"new(x.to_str()?)
r
OsString Path:"new(x.to_str()?)
r
PathBuf Path:"new(x.to_str()?)
Vec<u8> 1 ?
r
&str Path:"new(x)
&CStr Path:"new(x.to_str()?)
r
&OsStr Path:"new(x)
&Path x
&[u8] 1 ?
&[u8]
CString x.as_bytes()
OsString ?
PathBuf ?
1
https://cheats.rs/#_print Page 44 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Vec<u8> 1 &x
&str x.as_bytes()
&CStr x.to_bytes_with_nul()
&OsStr x.as_bytes() 2
&Path ?
&[u8] 1 x
Other
i
Short form x.into() possible if type can be inferred.
r
Short form x.as_ref() possible if type can be inferred.
1 You should, or must if call is unsafe , ensure raw data comes with a valid representation for the string type (e.g., UTF-8 data for a String ).
2 Only on some platforms std:"os:"<your_os>:"ffi:"OsStrExt exists with helper methods to get a raw &[u8] representation of the underlying OsStr . Use the rest of
the table to go from there, e.g.:
use std:"os:"unix:"ffi:"OsStrExt;
let bytes: &[u8] = my_os_str.as_bytes();
CString:"new(bytes)?
3
The c_char must have come from a previous CString . If it comes from FFI see &CStr instead.
4 No known shorthand as x will lack terminating 0x0 . Best way to probably go via CString .
String Output
How to convert types into a String , or output them.
APIs
Rust has, among others, these APIs to convert types to stringified output, collectively called format macros:
https://cheats.rs/#_print Page 45 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Method Notes
x.to_string() STD Produces String , implemented for any Display type.
Here fmt is string literal such as "hello {}" , that specifies output (compare "Formatting" tab) and
additional parameters.
Printable Types
STD STD
In format! and friends, types convert via trait Display "{}" or Debug "{:?}" , non exhaustive list:
Type Implements
String Debug, Display
CString Debug
OsString Debug
PathBuf Debug
Vec<u8> Debug
&CStr Debug
&OsStr Debug
&Path Debug
&[u8] Debug
! Debug, Display
() Debug
In short, pretty much everything is Debug ; more special types might need special handling or conversion ↑
to Display .
https://cheats.rs/#_print Page 46 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Formatting
Each argument designator in format macro is either empty {} , {argument} , or follows a basic syntax:
{ [argument] ':' [[fill] align] [sign] ['#'] [width [$]] ['.' precision [$]] [type] }
Element Meaning
argument Number ( 0 , 1 , …), variable '21 or name,'18 e.g., print!("{x}") .
fill The character to fill empty spaces with (e.g., 0 ), if width is specified.
align Left ( < ), center ( ^ ), or right ( > ), if width is specified.
sign Can be + for sign to always be printed.
# Alternate formatting, e.g., prettify Debug STD formatter ? or prefix hex with 0x .
width Minimum width (≥ 0), padding with fill (default to space). If starts with 0 , zero-padded.
precision Decimal digits (≥ 0) for numerics, or max width for non-numerics.
Interpret width or precision as argument identifier instead to allow for dynamic
$
formatting.
type Debug STD ( ? ) formatting, hex ( x ), binary ( b ), octal ( o ), pointer ( p ), exp ( e ) … see more.
https://cheats.rs/#_print Page 47 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Tooling
Project Anatomy
Basic project layout, and common files and folders, as used by cargo . ↓
Entry Code
.cargo/ Project-local cargo configuration, may contain config.toml . !
benches/ Benchmarks for your crate, run via cargo bench , requires nightly by default. *
examples/ Examples how to use your crate, they see your crate like external user would.
my_example.rs Individual examples are run like cargo run --example my_example .
lib.rs Default 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.
.clippy.toml Special configuration for certain clippy lints, utilized via cargo clippy !
rust-toolchain.toml Define toolchain override (channel, components, targets) for this project.
*
On stable consider Criterion.
Applications
fn main() {
println!("Hello, world!");
}
Libraries
https://cheats.rs/#_print Page 48 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
pub fn f() {} /5 Is a public item in root, so it's accessible from the outside.
mod m {
pub fn g() {} /5 No public path (`m` not public) from root, so `g`
} /5 is not accessible from the outside of the crate.
Unit Tests
fn f() -( u32 { 0 }
#,cfg(test)]
mod test {
use super:"f; /5 Need to import items from parent module. Has
/5 access to non-public members.
#,test]
fn ff() {
assert_eq!(f(), 0);
}
}
Integration Tests
#,test]
fn my_sample() {
assert_eq!(my_crate:"f(), 123); /5 Integration tests (and benchmarks) 'depend' to the
crate like
} /5 a 3rd party would. Hence, they only see public items.
Benchmarks
Build Scripts
https://cheats.rs/#_print Page 49 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
#,bench]
fn my_algo(b: &mut Bencher) {
b.iter(|| black_box(my_crate:"f())); /5 `black_box` prevents `f` from being optimized away.
}
fn main() {
/5 You need to rely on env. vars for target; `#,cfg(…)]` are for host.
let target_os = env:"var("CARGO_CFG_TARGET_OS");
}
*See
here for list of environment variables set.
Proc Macros!
use proc_macro:"TokenStream;
https://cheats.rs/#_print Page 50 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
/5 Cargo.toml
[package]
name = "my_crate"
version = "0.1.0"
[lib]
proc-macro = true
Module Trees
Module tree needs to be explicitly defined, is not implicitly built from file system tree.
Module tree root equals library, app, … entry point (e.g., lib.rs ).
A mod m {} defines module in-file, while mod m; will read m.rs or m/mod.rs .
Path of .rs based on nesting, e.g., mod a { mod b { mod c; }}} is either a/b/c.rs or a/b/c/mod.rs
.
Files not pathed from module tree root via some mod m; won't be touched by compiler!
Namespaces!
X (crate) const X: u8 = 1;
trait X {} static X: u8 = 1;
enum X {}
union X {}
struct X {}
← struct X; 1 →
← struct X(); 2 →
1
https://cheats.rs/#_print Page 51 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
1
Counts in Types and in Functions, defines type X and constant X .
2 Counts in Types and in Functions, defines type X and function X .
In any given scope, for example within a module, only one item item per namespace can exist, e.g.,
enum X {} and fn X() {} can coexist
struct X; and const X cannot coexist
With a use my_mod:"X; all items called X will be imported.
Due to naming conventions (e.g., fn and mod are lowercase by convention) and common sense (most
developers just don't name all things X ) you won't have to worry about these kinds in most cases. They
can, however, be a factor when designing macros.
Cargo
Commands and tools that are good to know.
Command Description
cargo init Create a new project for the latest edition.
cargo b uild Build the project in debug mode ( -- r elease for all optimization).
cargo run -p w Run main of sub-workspace w . Treats features more as you would expect.
cargo … --timings Show what crates caused your build to take so long.
cargo tree Show dependency graph.
cargo +{nightly, stable} … Use given toolchain for command, e.g., for 'nightly only' tools.
Here cargo b uild means you can either type cargo build or just cargo b ; and -- r elease means it can be replaced with -r .
These are optional rustup components. Install them with rustup component add [tool] .
Tool Description
cargo clippy Additional (lints) catching common API misuses and unidiomatic code.
https://cheats.rs/#_print Page 52 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Cross Compilation
Check target is supported.
Get from target vendor (Google, Apple, …), might not be available on all hosts (e.g., no iOS toolchain on Windows).
[target.aarch64-linux-android]
linker = "[PATH_TO_TOOLCHAIN]/aarch64-linux-android/bin/aarch64-linux-android-clang"
or
[target.aarch64-linux-android]
linker = "C:/[PATH_TO_TOOLCHAIN]/prebuilt/windows-x86_64/bin/aarch64-linux-android21-clang.cmd"
Set environment variables (optional, wait until compiler complains before setting):
set CC=C:\[PATH_TO_TOOLCHAIN]\prebuilt\windows-x86_64\bin\aarch64-linux-android21-clang.cmd
set CXX=C:\[PATH_TO_TOOLCHAIN]\prebuilt\windows-x86_64\bin\aarch64-linux-android21-clang.cmd
set AR=C:\[PATH_TO_TOOLCHAIN]\prebuilt\windows-x86_64\bin\aarch64-linux-android-ar.exe
…
Whether you set them depends on how compiler complains, not necessarily all are needed.
Some platforms / configurations can be extremely sensitive how paths are specified (e.g., \ vs / ) and quoted.
Tooling Directives
Special tokens embedded in source code used by tooling or preprocessing.
Macros
https://cheats.rs/#_print Page 53 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Documentation
https://cheats.rs/#_print Page 54 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
#%[globals]
Opt-Out's On Explanation
#-[no_std] C Don't (automatically) import std STD ; use core STD instead. REF
#-[no_implicit_prelude] CM Don't add prelude STD, need to manually import None , Vec , … REF
#-[no_main] C Don't emit main() in apps if you do that yourself. REF
Opt-In's On Explanation
#-[feature(a, b, c)] C Rely on features that may never get stabilized, c. Unstable Book.
Builds On Explanation
#-[windows_subsystem = "x"] C On Windows, make a console or windows app. REF !
Specifiy current crate name, e.g., when not using cargo . ? REF
#-[crate_name = "x"] C
!
#-[crate_type = "bin"] C Specifiy current crate type ( bin , lib , dylib , cdylib , …). REF !
#-[recursion_limit = "123"] C Set compile-time recursion limit for deref, macros, … REF !
#-[type_length_limit =
C Limits maximum number of type substitutions. REF !
"456"]
Handlers On Explanation
#,panic_handler] F Make some fn(&PanicInfo) -( ! app's panic handler. REF
#,alloc_error_handler] F Make some fn(Layout) -( ! the allocation failure handler.
STD
#,global_allocator] S Make static item impl. GlobalAlloc global allocator. REF
#&code]
Developer UX On Explanation
#,non_exhaustive] T Future-proof struct or enum ; hint it may grow in future. REF
REF
https://cheats.rs/#_print Page 55 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Codegen On Explanation
Nicely suggest compiler should inline function at call sites.
#,inline] F
REF
#,track_caller] F Allows fn to find caller STD for better panic messages. REF
Linking On Explanation
Use item name directly as symbol name, instead of mangling.
#,no_mangle] *
REF
#,no_link] X Don't link extern crate when only wanting macros. REF
#,link(name="x",
X Native lib to link against when looking up symbol. REF
kind="y")]
https://cheats.rs/#_print Page 56 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
#&quality]
Tests On Explanation
#,test] F Marks the function as a test, run with cargo test . REF
#,ignore = "msg"] F Compiles but does not execute some #,test] for now. REF
#,should_panic] F Test must panic!() to actually succeed. REF
#,bench] F Mark function in bench/ as benchmark for cargo bench . REF
Formatting On Explanation
#,rustfmt:"skip] * Prevent cargo fmt from cleaning up item.
#-[rustfmt:"skip:"macros(x)] CM … from cleaning up macro x .
#-[rustfmt:"skip:"attributes(x)] CM … from cleaning up attribute x .
Documentation On Explanation
#,doc = "Explanation"] * Same as adding a //4 doc comment.
Provide another name users can search for in the docs.
#,doc(alias = "other")] *
https://cheats.rs/#_print Page 57 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
#¯os]
Derives On Explanation
#,derive(X)] T Let some proc macro provide a goodish impl of trait X . REF
#&cfg]
Note, options can generally be set multiple times, i.e., the same key can show up with multiple
values. One can expect #,cfg(target_feature = "avx")] and #,cfg(target_feature = "avx2")] to
be true at the same time.
#,cfg(target_os = "macos")] * Operating system your code will run on. REF
#,cfg(target_family = "unix")] * Family operating system belongs to. REF
#,cfg(target_env = "msvc")] * How DLLs and functions are interfaced with on OS. REF
#,cfg(target_endian = "little")] * Main reason your cool new zero-cost protocol fails. REF
#,cfg(target_pointer_width = How many bits pointers, usize and CPU words have.
*
"64")] REF
build.rs
https://cheats.rs/#_print Page 59 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Types
u8 String Device
Type Values
u8 { 0u8, 1u8, …, 255u8 }
https://cheats.rs/#_print Page 60 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
It may be obvious but u8 , &u8 , &mut u8 , are entirely different from each other
Any t: T only accepts values from exactly T , e.g.,
f(0_u8) can't be called with f(&0_u8) ,
f(&mut my_u8) can't be called with f(&my_u8) ,
f(0_u8) can't be called with f(0_i8) .
Yes, 0 !J 0 (in a mathematical sense) when it comes to types! In a language sense, the operation
=:(0u8, 0u16) just isn't defined to prevent happy little accidents.
Type Values
u8 { 0u8, 1u8, …, 255u8 }
1Casts and coercions convert values from one set (e.g., u8 ) to another (e.g., u16 ), possibly adding CPU instructions to do so;
and in such differ from subtyping, which would imply type and subtype are part of the same set (e.g., u8 being subtype of u16
and 0_u8 being the same as 0_u16 ) where such a conversion would be purely a compile time check. Rust does not use
subtyping for regular types (and 0_u8 does differ from 0_u16 ) but sort-of for lifetimes.
2
Safety here is not just physical concept (e.g., &u8 can't be coerced to &u128 ), but also whether 'history has shown that such a
conversion would lead to programming errors'.
Implementations — impl S { }
u8 String Port
https://cheats.rs/#_print Page 61 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
impl Port {
fn f() { … }
}
Types usually come with inherent implementations, REF e.g., impl Port {} , behavior related to
type:
associated functions Port:"new(80)
methods port.close()
What's considered related is more philosophical than technical, nothing (except good taste) would
prevent a u8:"play_sound() from happening.
Traits — trait T { }
Traits …
are way to "abstract" behavior,
trait author declares semantically this trait means X,
other can implement ("subscribe to") that behavior for their type.
Think about trait as "membership list" for types:
u8 u8 char
… … …
trait ShowHex {
/5 Must be implemented according to documentation.
fn as_hex() -( String;
https://cheats.rs/#_print Page 62 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
⌾ Copy
trait Copy { }
⌾ Sized
ShowHex Trait
Self
Port
Visually, you can think of the type getting a "badge" for its membership:
u8 Device Port
⌾ Clone ⌾ Clone
⌾ Copy ⌾ ShowHex
⌾ Eat
Interfaces
https://cheats.rs/#_print Page 63 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
In other words, all membership must be exhaustively declared during type definition.
When using Venison , Santa can make use of behavior provided by Eat :
new Venison("rudolph").eat();
+
⌾ Eat
Traits
/5 Santa needs to import `Venison` to create it, and import `Eat` for trait method.
use food:"Venison;
use tasks:"Eat;
/5 Ho ho ho
Venison:"new("rudolph").eat();
*
To prevent two persons from implementing Eat differently Rust limits that choice to either Alice or Bob; that is, an
impl Eat for Venison may only happen in the crate of Venison or in the crate of Eat . For details see coherence. ?
Generics
Vec<u8> Vec<char>
Vec<u8> is type "vector of bytes"; Vec<char> is type "vector of chars", but what is Vec<> ?
Construct Values
Vec<u8> { [], [1], [1, 2, 3], … }
https://cheats.rs/#_print Page 64 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Vec<> -
Types vs type constructors.
Vec<>
Vec<> is no type, does not occupy memory, can't even be translated to code.
Vec<> is type constructor, a "template" or "recipe to create types"
allows 3rd party to construct concrete type via parameter,
only then would this Vec<UserType> become real type itself.
/5 S<K is type constructor with parameter T; user can supply any concrete type for T.
struct S<T> {
x: T
}
Some type constructors not only accept specific type, but also specific constant.
[T; n] constructs array type holding T type n times.
https://cheats.rs/#_print Page 65 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
⌾ Absolute ⌾ Clone
⌾ Dim ⌾ ShowHex
⌾ Mul
If T can be any type, how can we reason about (write code) for such a Num<T> ?
Parameter bounds:
limit what types (trait bound) or values (const bound ?) allowed,
we now can make use of these limits!
Trait bounds act as "membership check":
Absolute Trait
Self
u8
/5 Type can only be constructed for some `T` if that u16
/5 T is part of `Absolute` membership list.
struct Num<T> where T: Absolute { …
…
}
We add bounds to the struct here. In practice it's nicer add bounds to the respective impl blocks instead, see later this section.
https://cheats.rs/#_print Page 66 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
⌾ Mul ⌾ Mul
⌾ DirName
⌾ TwoD
struct S<T>
where
T: Absolute + Dim + Mul + DirName + TwoD
{ … }
When we write:
here is an implementation recipe for any type T (the impl <T> part),
where that type must be member of the Absolute + Dim + Mul traits,
you may add an implementation block to the type family S<> ,
containing the methods …
You can think of such impl<T> … {} code as abstractly implementing a family of behaviors. REF Most
notably, they allow 3rd parties to transparently materialize implementations similarly to how type
constructors materialize types:
https://cheats.rs/#_print Page 67 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Can also write "family implementations" so they apply trait to many types:
/5 Also implements Serialize for any type if that type already implements ToHex
impl<T> Serialize for T where T: ToHex { … }
They can be neat way to give foreign types functionality in a modular way if they just implement another
interface.
Advanced Concepts!
Notice how some traits can be "attached" multiple times, but others just once?
Port Port
⌾ From<u8> ⌾ Deref
type u8;
⌾ From<u16>
Why is that?
https://cheats.rs/#_print Page 68 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
From Deref
Self I Self O
u16 u8 Port u8
… …
Complex
Self [I] I1 I2 O1 O2
Various trait implementations. The last one is not valid as (NiceMonster, u16, String) has
already uniquely determined the outputs.
⌾ A<I> car.a(0_f32)
https://cheats.rs/#_print Page 69 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
T = u8;
Parameter choice (input vs. output) also determines who may be allowed to add members:
I parameters allow "familes of implementations" be forwarded to user (Santa),
O parameters must be determined by trait implementor (Alice or Bob).
trait A<I> { }
trait B { type O; }
A B
Self I Self O
Y … X u32
Santa may add more members by For given set of inputs (here Self ),
providing his own type for T . implementor must pre-select O .
No Additional Parameters
https://cheats.rs/#_print Page 70 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
trait Query {
fn search(&self, needle: &str);
}
postgres.search("SELECT …");
⌾ Query ⌾ Query
Input Parameters
trait Query<I> {
fn search(&self, needle: I);
}
postgres.search("SELECT …");
postgres.search(input.to_string());
sled.search(file);
⌾ Query<&str> ⌾ Query<T>
implementor would customize API in multiple ways for same Self type,
users may want ability to decide for which I -types behavior should be possible.
https://cheats.rs/#_print Page 71 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Output Parameters
trait Query {
type O;
fn search(&self, needle: Self:"O);
}
postgres.search("SELECT …".to_string());
sled.search(vec![0, 1, 2, 4]);
implementor would customize API for Self type (but in only one way),
users do not need, or should not have, ability to influence customization for specific Self .
As you can see here, the term input or output does not (necessarily) have anything to do with whether
I or O are inputs or outputs to an actual function!
trait Query<I> {
type O;
fn search(&self, needle: I) -( Self:"O;
}
postgres.search("SELECT …").to_uppercase();
sled.search(&[1, 2, 3, 4]).pop();
https://cheats.rs/#_print Page 72 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
O = CString;
users may want ability to decide for which I -types ability should be possible,
for given inputs, implementor should determine resulting output type.
A type T is Sized STD if at compile time it is known how many bytes it occupies, u8 and &[u8] are,
[u8] isn't.
Being Sized means impl Sized for T {} holds. Happens automatically and cannot be user impl'ed.
Types not Sized are called dynamically sized types BK NOM REF (DSTs), sometimes unsized.
Types without data are called zero sized types NOM (ZSTs), do not occupy space.
Example Explanation
struct A { x: u8 } Type A is sized, i.e., impl Sized for A holds, this is a 'regular' type.
Since [u8] is a DST, B in turn becomes DST, i.e., does not impl Sized
struct B { x: [u8] }
.
Type params have implicit T: Sized bound, e.g., C<A> is valid, C<B>
struct C<T> { x: T }
is not.
struct D<T: ?Sized> { x: T REF
Using ?Sized allows opt-out of that bound, i.e., D<B> is also valid.
}
struct E; Type E is zero-sized (and also sized) and will not consume memory.
Traits do not have an implicit Sized bound, i.e., impl F for B {} is
trait F { fn f(&self); }
valid.
trait F: Sized {} Traits can however opt into Sized via supertraits.↑
For Self -like params DST impl may still fail as params can't go on
trait G { fn g(self); }
stack.
?Sized
https://cheats.rs/#_print Page 73 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
struct S<T> { … }
/5 `'a is free parameter here (user can pass any specific lifetime)
struct S<'a> {
x: &'a u32
}
/5 In non-generic code, 'static is the only nameable lifetime we can explicitly put in here.
let a: S<'static>;
/5 Alternatively, in non-generic code we can (often must) omit 'a and have Rust determine
/5 the right value for 'a automatically.
let b: S;
* There are subtle differences, for example you can create an explicit instance 0 of a type u32 , but with the exception of
'static you can't really create a lifetime, e.g., "lines 80 - 100", the compiler will do that for you.
https://cheats.rs/#_print Page 74 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
bool ⌾ Copy
u8
Builder
⌾From<T>
From<T>
f32 ⌾⌾From<T> File
⌾ Deref
u16 char
type Tgt; String
&'a TT
&'a
&'a T
Vec<T>
Vec<T> Vec<T>
Vec<T>
Vec<T> f<T>() {}
PI
&mut 'a
&mut 'a T
&mut 'a TT
drop() {}
[T; n]
[T; n] dbg!
[T; n]
Your crat
Examples of traits and types, and which traits you can implement for which type.
Type Conversions
How to get B when you have A ?
Intro
Computation (Traits)
https://cheats.rs/#_print Page 75 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
fn f(x: A) -( B {
/5 How can you obtain B from A?
}
Method Explanation
Identity Trivial case, B is exactly A .
1
While both convert A to B , coercions generally link to an unrelated B (a type "one could reasonably expect to have different
methods"), while subtyping links to a B differing only in lifetimes.
fn f(x: A) -( B {
x.into()
}
Bread and butter way to get B from A . Some traits provide canonical, user-computable type relations:
Casts
https://cheats.rs/#_print Page 76 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
fn f(x: A) -( B {
x as B
}
Convert types with keyword as if conversion relatively obvious but might cause issues. NOM
A B Example Explanation
Pointer Pointer device_ptr as *const u8 If *A , *B are Sized .
Pointer Integer device_ptr as usize
Where Pointer , Integer , Number are just used for brevity and actually mean:
Opinion — Casts, esp. Number - Number , can easily go wrong. If you are concerned with correctness,
consider more explicit methods instead.
Coercions
fn f(x: A) -( B {
x
}
https://cheats.rs/#_print Page 77 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
A B Explanation
&mut T &T Pointer weakening.
&mut T *mut T -
&T *const T -
*mut T *const T -
&T &U Deref, if impl Deref<Target=U> for T .
T U Unsizing, if impl CoerceUnsized<U> for T .2
T V Transitivity, if T coerces to U and U to V .
|x| x + x fn(u8) -( u8 Non-capturing closure, to equivalent fn pointer.
1
Substantially meaning one can regularly expect a coercion result B to be an entirely different type (i.e., have
entirely different methods) than the original type A .
2
Does not quite work in example above as unsized can't be on stack; imagine f(x: &A) -( &B instead.
Unsizing works by default for:
[T; n] to [T]
T to dyn Trait if impl Trait for T {} .
Foo<…, T, …> to Foo<…, U, …> under arcane circumstances.
Subtyping!
fn f(x: A) -( B {
x
}
Automatically converts A to B for types only differing in lifetimes NOM - subtyping examples:
https://cheats.rs/#_print Page 78 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Box<&'static
Box<&'a u8> Invalid, Box with transient may not be with forever.
u8>
Box<&'a mut u8> Box<&'a u8> Invalid, see table below, &mut u8 never was a &u8 .
Cell<&'static u8> Cell<&'a u8> Invalid, Cell are never something else; invariant.
If fn needs forever it may choke on transients;
fn(&'static u8) fn(&'a u8)
contravar.
fn(&'static But sth. that eats transients can be(!) sth. that eats
fn(&'a u8)
u8) forevers.
for<'r> fn(&'r Higher-ranked type for<'r> fn(&'r u8) is also
fn(&'a u8)
u8) fn(&'a u8).
A B Explanation
u16 u8 Obviously invalid; u16 should never automatically be u8 .
Invalid by design; types w. different data still never subtype even if they
u8 u16
could.
&'a mut &'a
Trojan horse, not subtyping; but coercion (still works, just not subtyping).
u8 u8
Variance!
fn f(x: A) -( B {
x
}
Automatically converts A to B for types only differing in lifetimes NOM - subtyping variance rules:
Construct1 'a T U
https://cheats.rs/#_print Page 79 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Box<T> covariant
Cell<T> invariant
fn(T) -( U contravariant covariant
*const T covariant
*mut T invariant
Covariant means if A is subtype of B , then T[A] is subtype of T[B] .
Contravariant means if A is subtype of B , then T[B] is subtype of T[A] .
Invariant means even if A is subtype of B , neither T[A] nor T[B] will be subtype of the other.
1 Compounds like struct S<T> {} obtain variance through their used fields, usually becoming invariant if multiple variances
are mixed.
In other words, 'regular' types are never subtypes of each other (e.g., u8 is not subtype of u16 ),
and a Box<u32> would never be sub- or supertype of anything. However, generally a Box<A> , can be
subtype of Box<B> (via covariance) if A is a subtype of B , which can only happen if A and B are 'sort of
the same type that only differed in lifetimes', e.g., A being &'static u32 and B being &'a u32 .
Coding Guides
Idiomatic Rust
If you are used to Java or C, consider these.
Idiom Code
Think in Expressions y = if x { a } else { b };
y = loop { break 5 };
fn f() -( u32 { 0 }
names.iter().filter(|x| x.starts_with("A"))
get_option()?.run()?
https://cheats.rs/#_print Page 80 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Don't Panic Panics are not exceptions, they suggest immediate process abortion!
Only panic on programming error; use Option<T> STD or Result<T,E> STD otherwise.
If clearly user requested, e.g., calling obtain() vs. try_obtain() , panic ok too.
Generics in Moderation A simple <T: Bound> (e.g., AsRef<Path> ) can make your APIs nicer to use.
Split Implementations Generics like Point<T> can have separate impl per T for some specialization.
Implement Traits #,derive(Debug, Copy, …)] and custom impl where needed.
Add doc tests BK ( ``` my_api:"f() ``` ) to ensure docs match code.
Documentation Annotate your APIs with doc comments that can show up on docs.rs.
Don't forget to include a summary sentence and the Examples heading.
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:
Basics
Construct Explanation
async Anything declared async always returns an impl Future<Output=_> . STD
async fn f() {} Function f returns an impl Future<Output=()> .
let sm = f();
Calling f() that is async will not execute f , but produce state machine sm
12
https://cheats.rs/#_print Page 81 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
.12
sm = async { g() }; Likewise, does not execute the { g() } block; produces state machine.
runtime.block_on(sm); Outside an async {} , schedules sm to actually run. Would execute g() . 3 4
Inside an async {} , run sm until complete. Yield to runtime if sm not
sm.await
ready.
1 Technically async transforms following code into anonymous, compiler-generated state machine type; f() instantiates that
machine.
2
The state machine always impl Future , possibly Send & co, depending on types used inside async .
3 State machine driven by worker thread invoking Future:"poll() via runtime directly, or parent .await indirectly.
4 Rust doesn't come with runtime, need external crate instead, e.g., tokio. Also, more helpers in futures crate.
Execution Flow
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:
Caveats
With the execution flow in mind, some considerations when writing code inside an async construct:
Constructs 1 Explanation
sleep_or_block(); Definitely bad , never halt current thread, clogs executor.
https://cheats.rs/#_print Page 82 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
set_TL(a); x.await; Definitely bad , await may return from other thread, thread local
TL();
invalid.
s.no(); x.await;
Maybe bad , await will not return if Future dropped while waiting. 2
s.go();
Rc:"new(); x.await;
Non- Send types prevent impl Future from being Send ; less compatible.
rc();
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.
2Since 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.
Closures in APIs
There is a subtrait relationship Fn : FnMut : FnOnce . That means a closure that implements Fn STD
also implements FnMut and FnOnce .
Likewise a closure that implements FnMut STD also implements FnOnce . STD
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.
|| { &s; } FnOnce , FnMut , Fn May 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.
F: FnMut Allows g() to change caller state. Caller may not reuse captures during g() .
https://cheats.rs/#_print Page 83 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Safe Code
Safe Code
Safe has narrow meaning in Rust, vaguely 'the intrinsic prevention of undefined behavior (UB)'.
Intrinsic means the language won't allow you to use itself to cause UB.
Making an airplane crash or deleting your database is not UB, therefore 'safe' from Rust's
perspective.
Writing to /proc/[pid]/mem to self-modify your code is also 'safe', resulting UB not caused
intrinsincally.
let y = x + x; /5 Safe Rust only guarantees the execution of this code is consistent with
print(y); /5 'specification' (long story …). It does not guarantee that y is 2x
/5 (X:"add might be implemented badly) nor that y is printed (Y:"fmt may panic).
Unsafe Code
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 trust
you.
By itself unsafe code is not bad, but dangerous, and needed for FFI or exotic data structures.
Undefined Behavior
As mentioned, unsafe code implies special promises to the compiler (it wouldn't need be unsafe
https://cheats.rs/#_print Page 84 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
otherwise).
Failure to uphold any promise makes compiler produce fallacious code, execution of which leads to
UB.
After triggering undefined behavior anything can happen. 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, invalid and should never exist.
if maybe_true() {
let r: &u8 = unsafe { &*ptr:"null() }; /5 Once this runs, ENTIRE app is undefined.
Even if
} else { /5 line seemingly didn't do anything, app
might now run
println!("the spanish inquisition"); /5 both paths, corrupt database, or anything else.
}
Unsound Code
Unsound Code
Any safe Rust that could (even only theoretically) produce UB for any user input is always unsound.
As is unsafe code that may invoke UB on its own accord by violating above-mentioned promises.
Unsound code is a stability and security risk, and violates basic assumption many Rust users have.
!
!
https://cheats.rs/#_print Page 85 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Adversarial Code !
!
Adversarial code is safe 3rd party code that compiles but does not follow API expectations, and might interfere with your own (safety)
guarantees.
macro_rules! m { … } Do all of the above; call site can have weird scope.
impl std:"… for S {} Any trait impl , esp. std:"ops may be broken. In particular …
impl Deref for S {} May randomly Deref , e.g., s.x !J s.x , or panic.
impl Eq for S {} May cause s !J s ; panic; must not use s in HashMap & co.
impl Hash for S {} May violate hashing rules; panic; must not use s in HashMap & co.
impl Ord for S {} May violate ordering rules; panic; must not use s in BTreeMap & co.
impl Index for S {} May randomly index, e.g., s[x] !J s[x] ; panic.
impl Drop for S {} May run code or panic end of scope {} , during assignment s = new_s .
panic!() User code can panic any time, resulting in abort or unwind.
catch_unwind(|| s.f(panicky)) Also, caller might force observation of broken state in s .
Implications
Generic code cannot be safe if safety depends on type cooperation w.r.t. most ( std:" ) traits.
If type cooperation is needed you must use unsafe traits (prob. implement your own).
You must consider random code execution at unexpected places (e.g., re-assignments, scope end).
You may still be observable after a worst-case panic.
As a corollary, safe-but-deadly code (e.g., airplane_speed<T>() ) should probably also follow these guides.
API Stability
When updating an API, these changes can break client code.RFC Major changes ( ) are definitely breaking, while minor changes ( )
might be breaking:
https://cheats.rs/#_print Page 86 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
Crates
Making a crate that previously compiled for stable require nightly.
Modules
Renaming / moving / removing any public items.
Adding new public items, as this might break code that does use your_crate:"* .
Structs
Adding private field when all current fields public.
Going from a tuple struct with all private fields (with at least one field) to a normal struct, or vice versa.
Enums
Adding new variants; can be mitigated with early #,non_exhaustive] REF
Traits
Adding a non-defaulted item, breaks all existing impl T for S {} .
Any non-trivial change to item signatures, will affect either consumers or implementors.
Adding a defaulted item; might cause dispatch ambiguity with other existing trait.
Traits
Implementing any "fundamental" trait, as not implementing a fundamental trait already was a promise.
Implementing any non-fundamental trait; might also cause dispatch ambiguity.
Inherent Implementations
Adding any inherent items; might cause clients to prefer that over trait fn and produce compile error.
Loosening bounds.
Generalizing to generics.
Signatures in Functions
Adding / removing arguments.
Generalizing to generics.
Behavioral Changes
https://cheats.rs/#_print Page 87 of 88
Rust Language Cheat Sheet 7/22/23, 12:55 PM
/ Changing semantics might not cause compiler errors, but might make clients do wrong thing.
https://cheats.rs/#_print Page 88 of 88