https://cheats.rs/ Fork me on GitHub Ferris holding a cheat sheet. Rust Language Cheat Sheet 25.04.2021 Contains clickable links to The Book ^BK, Rust by Example ^EX, Std Docs ^STD, Nomicon ^NOM, Reference ^REF. Clickable symbols ^BK The Book ^EX Rust by Example ^STD Std Docs ^NOM Nomicon ^REF Reference ^RFC Official RFC documents ^ The internet ^| On this page, above ^| On this page, below Other symbols ^[?] Largely deprecated ^'18 Has minimum edition requirement ^ Requires Rust nightly (or is incomplete) ^ Intentionally wrong example or pitfall ^ Slightly esoteric, rarely used or advanced ^ Something with outstanding utility ^? Is missing good link or explanation ^ Opinionated Fira Code Ligatures (..=, =>) Expand all the things? Night Mode Language Constructs * Data Structures * References & Pointers * Functions & Behavior * Control Flow * Organizing Code * Type Aliases and Casts * Macros & Attributes * Pattern Matching * Generics & Constraints * Strings & Chars * Documentation * Miscellaneous Behind the Scenes * The Abstract Machine * Memory & Lifetimes * Language Sugar * Types, Traits, Generics Data Layout * Basic Types * Custom Types * References & Pointers * Closures * Standard Library Types Standard Library * One-Liners * Thread Safety * Dynamically / Zero Sized Types * Iterators * Number Conversions * String Conversions * String Output Tooling * Project Anatomy * Cargo * Cross Compilation * Tooling Directives Coding Guides * Idiomatic Rust * Async-Await 101 * Closures in APIs * Unsafe, Unsound, Undefined * API Stability Misc * Links & Services * Printing & PDF Hello, Rust!^url If you are new to Rust, or if you want to try the things below: (*) Hello World fn main() { println!("Hello, world!"); } Service provided by play.rust-lang.org ^ >[?] Edit & Run ( ) Strengths Things Rust does measurably really well * Compiled code about same performance as C / C++, and excellent memory and energy efficiency. * Can avoid 70% of all safety issues present in C / C++, and most memory issues. * Strong type system prevents data races, brings 'fearless concurrency' (amongst others). * Seamless C interop, and dozens of supported platforms (based on LLVM). * "Most loved language" for 5 years in a row. * Modern tooling: cargo (builds just work), clippy (300+ code quality lints), rustup (easy toolchain mgmt). ( ) Weaknesses Points you might run into * Steep learning curve;^1 compiler enforcing (esp. memory) rules that would be "best practices" elsewhere. * Missing Rust-native libs in some domains, target platforms (esp. embedded), IDE features.^1 * Longer compile times than "similar" code in other languages.^1 * No formal language specification, can prevent legal use in some domains (aviation, medical, ...). * Careless (use of unsafe in) libraries can secretly break safety guarantees. ^1 Compare Rust Survey. ( ) Installation Download * Get installer from rustup.rs (highly recommended for any platform) IDEs * IntelliJ (free) or CLion (paid) with IntelliJ Rust ^ * Visual Studio Code with rust-analyzer ( ) First Steps Modular Beginner Resources * Tour of Rust - Live code and explanations, side by side. * Rust in Easy English - 60+ concepts, simple English, example-driven. In addition, have a look at the ususal suspects. ^BK ^EX ^STD Opinion ^ -- If you have never seen or used any Rust it might be good to visit one of the links above before continuing; the next chapter might feel a bit terse otherwise. Data Structures^url Data types and memory locations defined via keywords. Example Explanation struct S {} Define a struct ^BK ^EX ^STD ^REF with named fields. struct S { x: Define struct with named field x of type T. T } struct S (T); Define "tupled" struct with numbered field .0 of type T. struct S; Define zero sized ^NOM unit struct. Occupies no space, optimized away. enum E {} Define an enum ^BK ^EX ^REF , c. algebraic data types, tagged unions. enum E { A, B Define variants of enum; can be unit- A, tuple- (), C {} } 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 ^BK ^EX and mutable borrow.^2 x = y; Moves y to x, invalidating y if T is not Copy, ^ STD and copying y otherwise. ^1 Bound variables ^BK ^EX ^REF live on stack for synchronous code. In async {} code they become part async's state machine, may reside on heap. ^2 Technically mutable and immutable are misnomer. Immutable binding or shared reference may still contain Cell ^STD, giving interior mutability. 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 { 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. S If S is unit struct S; or use'ed enum E::S create value of S. E::C { x: Create enum variant C. Other methods above also work. y } () 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. ^EX ^STD ^ 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, here w. usize. Implementable with Index, IndexMut. x Same, via range (here full range), also x[a..b], x[a..=b], [..] ... c. below. a..b Right-exclusive range ^STD ^REF creation, e.g., 1..3 means 1, 2. ..b Right-exclusive range to ^STD without starting point. a..=b Inclusive range, ^STD 1..=3 means 1, 2, 3. ..=b Inclusive range from ^STD without starting point. .. Full range, ^STD usually means the whole collection. s.x Named field access, ^REF might try to Deref if x not part of type S. s.0 Numbered field access, used for tuple types S (T). ^* For now,^RFC pending completion of tracking issue. References & Pointers^url Granting access to un-owned memory. Also see section on Generics & Constraints. Example Explanation &S Shared reference ^BK ^STD ^NOM ^REF (space for holding any &s). &[S] Special slice reference that contains (address, length). &str Special string slice reference that contains (address, length). &mut S Exclusive reference to allow mutability (also &mut [S], &mut dyn S, ...) &dyn T Special trait object ^BK reference that contains (address, vtable). &s Shared borrow ^BK ^EX ^STD (e.g., address, len, vtable, ... of this s, like 0x1234). &mut s Exclusive borrow that allows mutability. ^EX *const S Immutable raw pointer type ^BK ^STD ^REF w/o memory safety. *mut S Mutable raw pointer type w/o memory safety. &raw const s Create raw pointer w/o going through reference; c. ptr:addr_of!() ^STD ^ ^ &raw mut s Same, but mutable. ^ Raw ptrs. are needed for unaligned, packed fields. ^ ref s Bind by reference. ^EX ^[?] let ref r = Equivalent to let r = &s. s; let S { ref Mutable ref binding (let x = &mut s.x), shorthand mut x } = s; destructuring ^| version. *r Dereference ^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 = *r; Won't work ^ if *r is not Copy, as that would move and leave empty place. s = *my_box; Special case^ for Box that can also move out Box'ed content if it isn't Copy. 'a A lifetime parameter, ^BK ^EX ^NOM ^REF duration of a flow in static analysis. &'a S Only accepts an address holding an s; addr. existing 'a or longer. &'a mut S Same, but allow content of address to be changed. struct S<'a> Signals S will contain address with lifetime 'a. {} Creator of S decides 'a. trait T<'a> Signals a S which impl T for S might contain {} address. fn f<'a>(t: & Same, for function. Caller decides 'a. 'a T) 'static Special lifetime lasting the entire program execution. Functions & Behavior^url Define units of code and their abstractions. Example Explanation trait T {} Define a trait; ^BK ^EX ^REF common behavior others can implement. 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, e.g., methods. 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, ^BK ^EX e.g., within an impl S {}. {} 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 async fn f Same, but make f return an impl Future. () -> S {} async { x } Used within a function, make { x } an impl Future . fn() -> S Function pointers, ^BK ^STD ^REF memory holding address of a callable. Fn() -> S Callable Trait ^BK ^STD (also FnMut, FnOnce), implemented by closures, fn's ... || {} A closure ^BK ^EX ^REF that borrows its captures. ^ REF |x| {} Closure with a bound parameter x. |x| x + x Closure without block expression; may only consist of single expression. move |x| x Closure taking ownership of its captures. + y return || Closures sometimes look like logical ORs (here: true return a closure). unsafe If you enjoy debugging segfaults Friday night; unsafe code. ^| ^BK ^EX ^NOM ^REF unsafe f() Sort-of means "can cause UB, ^| YOU must check {} requirements". unsafe {} Guarantees to compiler "I have checked requirements, trust me". Control Flow^url Control execution within a function. Example Explanation 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. break Break expression ^REF to exit a loop. break x Same, but make x value of the loop expression (only in actual loop). break Exit not only this loop, but the enclosing one marked 'label with 'label. break Same, but make x the value of the enclosing loop marked 'label x with 'label. continue Continue expression ^REF to the next loop iteration of this loop. continue Same but instead of this loop, enclosing loop marked 'label with 'label. x? If x is Err or None, return and propagate. ^BK ^EX ^STD ^REF x.await Only works inside async. Yield flow until Future ^STD or Stream x ready. ^REF ^'18 return x Early 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(& Same as x.f(). mut x) 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(). ::f() Organizing Code^url Segment projects into smaller units and minimize dependencies. Example Explanation mod m {} Define a module, ^BK ^EX ^REF get definition from inside {}. ^| mod m; Define a module, get definition from m.rs or m/mod.rs. ^| a::b Namespace path ^EX ^REF to element b within a (mod, enum, ...). ::b Search b relative to crate root. ^[?] crate::b Search b relative to crate root. ^'18 self::b Search b relative to current module. super::b Search 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 Visible at most in current crate. (crate) T pub(self) Visible at most in current module. T pub Visible at most in parent. (super) T pub(in Visible at most in a::b. a::b) T extern crate Declare dependency on external crate ^BK ^REF ^[?] ; a; 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 Define function to be exported with ABI (e.g., "C") to f() {} FFI. Type Aliases and Casts^url Short-hand names of types, and methods to convert one type to another. 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 Method subject in fn f(self) {}, same as fn f(self: Self) {}. &self Same, but refers to self as borrowed, same as f(self: &Self) &mut self Same, but mutably borrowed, same as f(self: &mut Self) self: Box Arbitrary self type, add methods to smart pointers (my_box.f_of_self()). S as T Disambiguate ^BK ^REF type S as trait T, e.g., ::f(). S as R In use of symbol, import S as R, e.g., use a::S as R. x as u32 Primitive cast ^EX ^REF, may truncate and be a bit surprising. ^NOM Macros & Attributes^url Code generation constructs expanded before the actual compilation happens. Example Explanation m!() Macro ^BK ^STD ^REF invocation, also m!{}, m![] (depending on macro). #[attr] Outer attribute. ^EX ^REF, annotating the following item. #! Inner attribute, annotating the upper, surrounding item. [attr] Inside Explanation Macros $x:ty Macro capture (here a type). $x Macro substitution, e.g., use the captured $x:ty from above. $(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: <<. See tooling directives ^| for details. Pattern Matching^url 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 { x } = Only x will be bound to value s.x. s; let (_, b, _) Only b will be bound to value abc.1. = abc; let (a, ..) = Ignoring 'the rest' also works. abc; let (.., a, b) Specific bindings take precedence over 'the = (1, 2); rest', here a is 1, b is 2. let Some(x) = Won't work ^ if pattern can be refuted ^REF, use get(); if let instead. if let Some(x) = Branch if pattern can be assigned (e.g., enum get() {} variant), syntactic sugar. ^* while let Some(x) = Equiv.; here keep calling get(), run {} as long get() {} 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) => {}, _ => () }. Pattern matching arms in match expressions. Left side of these arms can also be found in let expressions. Within Match Arm Explanation 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 values (only accepts s {} with s.x of 0 and s.y of 1). S { x: a, y: b } => Match struct with any(!) values and bind s.x to a {} and s.y to b. S { x, y } => Same, but shorthand with s.x and s.y bound as x {} and y respectively. 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". 0 | 1 => {} Pattern alternatives, or-patterns. ^RFC E::A | E::Z Same, but on enum variants. E::C {x} | Same, but bind x if all variants have it. E::D {x} (a, 0) => {} Match tuple with any value for a and 0 for second. [a, 0] => {} Slice pattern, ^REF ^ match array with any value for a and 0 for second. [1, ..] => {} Match array starting with 1, any value for rest; subslice pattern. ^? [1, .., 5] => Match array starting with 1, ending with 5. {} [1, x @ .., 5] Same, but also bind x to slice representing => {} middle (c. next entry). x @ 1..=5 => {} Bind matched to x; pattern binding, ^BK ^EX ^REF here x would be 1, 2, ... or 5. Err(x @ Error Also works nested, here x binds to Error, esp. {..}) => {} useful with if below. S { x } if x > 10 Pattern match guards, ^BK ^EX ^REF condition must => {} be true as well to match. Generics & Constraints^url Generics combine with type constructors, traits and functions to give your users more flexibility. Example Explanation S A generic ^BK ^EX type with a type parameter (T is placeholder name here). S Type short hand trait bound ^BK ^EX specification (R must be actual trait). T: R, P: S Independent trait bounds (here one for T and one for P). T: R, S Compile error, ^ you probably want compound bound R + S below. T: R + S Compound trait bound ^BK ^EX, T must fulfill R and S. T: R + 'a Same, but w. lifetime. T must fulfill R, if T has lifetimes, must outlive 'a. T: ?Sized Opt out of a pre-defined trait bound, here Sized. ^? T: 'a Type lifetime bound ^EX; if T has references, they must outlive 'a. T: 'static Same; does esp. not mean value t will ^ live 'static, only that it could. 'b: 'a Lifetime 'b must live at least as long as (i.e., outlive) 'a bound. S Generic const bound; ^? user of type S can provide constant value N. ^ S<10> Where used, const bounds can be provided as primitive values. S<{5+5}> Expressions must be put in curly brackets. S where T: R Almost same as S but more pleasant to read for longer bounds. S where u8: Also allows you to make conditional statements R involving other types. S Default type parameter ^BK for associated type. S<'_> Inferred anonymous lifetime; asks compiler to 'figure it out' if obvious. S<_> Inferred anonymous type, e.g., as let x: Vec<_> = iter.collect() S:: Turbofish ^STD call site type disambiguation, e.g. f::(). trait T {} 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 S {} Implement functionality for any T in S, here T type parameter. impl S {} Implement functionality for exactly S, here T specific type (e.g., S). fn f() -> impl T Existential 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(x: &S). fn f(x: &dyn T) Marker for dynamic dispatch, ^BK ^REF f will not be monomorphized. fn f() where Self: In trait T {}, make f accessible only on types R; known to also impl R. fn f() where Esp. useful w. default methods (non dflt. would Self: R {} need be impl'ed anyway). for<'a> Higher-ranked trait bounds. ^NOM ^REF ^ trait T: for Any S that impl T would also have to fulfill R <'a> R<'a> {} for any lifetime. Strings & Chars^url Rust has several ways to create textual values. Example Explanation "..." String literal, ^REF^, 1 UTF-8, will interpret \n as line break 0xA, ... r"..." Raw string literal. ^REF^, 1 UTF-8, won't interpret \ n, ... r#"..."# Raw string literal, UTF-8, but can also contain ". Number of # can vary. b"..." Byte string literal; ^REF^, 1 constructs ASCII [u8], not a string. br"...", br# 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 ^1 Supports multiple lines out of the box. Just keep in mind Debug^| (e.g., dbg!(x) and println!("{:?}", x)) might render them as \n, while Display^| (e.g., println!("{}", x)) renders them proper. Documentation^url Debuggers hate him. Avoid bugs with this one weird trick. Example Explanation /// Outer line doc comment, ^BK ^EX ^REF use these on types, traits, functions, ... //! Inner line doc comment, mostly used at start of file to document module. // Line comment, use these to document code flow or internals. /*...*/ Block comment. /**...* Outer block doc comment. / /*!...* Inner block doc comment. / Tooling directives ^| outlines what you can do inside doc comments. Miscellaneous^url These sigils did not fit any other category but are good to know nonetheless. Example Explanation ! Always empty never type. ^ ^BK ^EX ^STD ^REF _ Unnamed variable binding, e.g., |x, _| {}. let _ = x; Unnamed assignment is no-op, does not ^ move out x or preserve scope! _x Variable binding explicitly marked as unused. 1_234_567 Numeric separator for visual clarity. 1_u8 Type specifier for numeric literals ^EX ^REF (also i8, u16, ...). 0xBEEF, 0o777, Hexadecimal (0x), octal (0o) and binary (0b) integer 0b1001 literals. r#foo A raw identifier ^BK ^EX for edition compatibility. x; Statement ^REF terminator, c. expressions ^EX ^REF Common Operators^url Rust supports most operators you would expect (+, *, %, =, ==, ...), including overloading. ^STD Since they behave no differently in Rust we do not list them here. --------------------------------------------------------------------- Behind the Scenes^url Arcane knowledge that may do terrible things to your mind, highly recommended. The Abstract Machine^url Like C and C++, Rust is based on an abstract machine. Rust - CPU ^ Less correctish. Rust - Abstract Machine - CPU More correctish. The abstract machine * 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, * forms a contract between programmer and machine, * and exploits all of the above for optimizations. Without AM^* With AM 0xffff_ffff would make a valid Memory more than just bits. char. ^ 0xff and 0xff are same pointer. ^ Pointers can come from different domains. Any r/w pointer on 0xff always Read and write reference may not fine. ^ exist same time. Null reference is just 0x0 in Holding 0x0 in reference summons some register. ^ Cthulhu. ^* Things people may incorrectly assume they should get away with if Rust targeted CPU directly, and more correct counterparts. Practically this means: + before assuming your CPU will do A when writing B you need positive proof via documentation(!), + if you don't have that any physical behavior is coincidental, + violate the abtract machine's contract and the optimizer makes your CPU do something entirely else -- undefined behavior.^| Memory & Lifetimes^url Why moves, references and lifetimes are how they are. (*) Types & Moves Application Memory S(1) Application Memory * Application memory in itself is just array of bytes. * Operating environment usually segments that, amongst others, into: + stack (small, low-overhead memory,^1 most variables go here), + heap (large, flexible memory, but always handled via stack proxy like Box), + static (most commonly used as resting place for str part of & str), + code (where bitcode of your functions reside). * Programming languages such as Rust give developers tools to: + define what data goes into what segment, + express a desire for bitcode with specific properties to be produced, + protect themselves from errors while performing these operations. * Most tricky part is tied to how stack evolves, which is our focus . ^1 While for each part of the heap someone (the allocator) needs to perform bookkeeping at runtime, the stack is trivially managable: take a few bytes more while you need them, they will be discarded once you leave. The (for performance reasons desired) simplicity of this appraoch, along with the fact that you can tell others about such transient locations (which in turn might want to access them long after you left), form the very essence of why lifetimes exist; and are the subject of the rest of this chapter. Variables S(1) S(1) a 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 that the term variable has some linguistic ambiguity,^2 it can mean: 1. the name of the location ("rename that variable"), 2. the location itself, 0x7 ("tell me the address of that variable"), 3. the 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. ^2 It is the author's opinion ^ that this ambiguity related to variables (and lifetimes and scope later) are some of the biggest contributors to the confusion around learning the basics of lifetimes. Whenever you hear one of these terms ask yourself "what exactly is meant here?" Move Semantics S(1) a t Moves let a = t; * This will move value within t to location of a, or copy it, if S is Copy. * After move location t is invalid and cannot be read anymore. + Technically the bits at that location are not really empty, but undefined. + If you still had access to t (via unsafe) they might still look like valid S, but any attempt to use them as valid S is undefined behavior. ^| * We do not cover Copy types explicitly here. They change the rules a bit, but not much: + They won't be dropped + They never leave behind an 'empty' variable location. Type Safety S(1) M { ... } [?] a c Type Safety let c: S = M::new(); * The type of a variable serves multiple important purposes, it: 1. dictates how the underlying bits are to be interpreted, 2. allows only well-defined operations on these bits 3. prevents random other values or bits from being written to that location. * Here assignment fails to compile since the bytes of M::new() cannot be converted to form of type S. * Conversions between types will always fail in general, unless explicit rule allows it (coercion, cast, ...). As an excercise to the reader, any time you see a value of type A being assignable to a location of some type not-exactly-A you should ask yourself: through what mechanism is this possible? Scope & Drop S(1)V C(2) S(2)V S(3) t Scope & Drop { let mut c = S(2); c = S(3); // <- Drop called on `c` before assignment. let t = S(1); let a = t; } // <- 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(). ( ) Call Stack Stack Frame S(1) a x Function Boundaries fn f(x: S) { ... } let a = S(1); // <- We are here f(a); * 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) } // <- 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. Validity of Variables S(1) M { } a x m Repurposing Memory fn f(x: S) { if once() { f(x) } let m = M::new() // <- 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. ( ) References & Pointers Reference Types V 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: let r: &S = &a; let r = &a; (Mutable) References V S(2) 0x3 S(1) a r d Access to Non-Owned Memory let mut a = S(1); let r = &mut a; let d = r.clone(); // Valid to clone (or copy) from r-target. *r = S(2); // Valid to set new S value to r-target. * 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). V S(2) 0x3 M { x } [?] [?] a r d References Guard Referents let mut a = ...; let r = &mut a; let d = *r; // Invalid to move out value, `a` would be empty. *r = M::new(); // 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). V C(2) 0x3 c p Raw Pointers let p: *const S = questionable_origin(); * In contrast to references, pointers come with almost no guarantees. * They may point to invalid or non-existent data. * Dereferencing them is unsafe, and treating an invalid *p as if it were valid is undefined behavior. ^| ( ) Lifetime Basics C(2) 0x3 "Lifetime" of Things * Every entity in a program has some time it is alive. * Loosely speaking, this alive time can be^1 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. lifetime^2 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. ^1 There 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 ... V S(0) S(1) S(2) 0xa a b c r Meaning of r: &'c S * Assume you got a r: &'c S from somewhere it means: + r holds an address of some S, + any address r points to must and will exist for at least 'c, + the variable r itself cannot live longer than 'c. V S(0) S(3) S(2) 0x6 [?] a b c r Typelikeness of Lifetimes { let b = S(3); { let c = S(2); let r: &'c S = &c; // Does not quite work since we can't name lifetimes of local { // variables in a function body, but very same principle applies let a = S(0); // to functions next page. r = &a; // Location of `a` does not live sufficient many lines -> not ok. r = &b; // Location of `b` lives all lines of `c` and more -> ok. } } } * Assume you got a mut r: &mut 'c S from somewhere. + That is, a mutable location that can hold a mutable reference. * As mentioned, that reference must guard the targeted memory. * However, the 'c part, like a type, also guards what is allowed into r. * Here assiging &b (0x6) to r is valid, but &a (0x3) would not, as only &b lives equal or longer than &c. V S(0) S(2) 0x6 S(4) [?] a b c Borrowed State let mut b = S(0); let r = &mut b; b = S(4); // Will fail since `b` in 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 addess 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. ( ) Lifetimes in Functions S(0) S(1) S(2) ? 0x6 0xa a b c r x y Function Parameters fn f(x: &S, y:&S) -> &u8 { ... } let b = S(1); let c = S(2); let r = f(&b, &c); * 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(0) S(1) S(2) ? 0x6 0xa a b c r x y Problem of 'Borrowed' Propagation let b = S(1); let c = S(2); let r = f(&b, &c); let a = b; // Are we allowed to do this? let a = c; // Which one is _really_ borrowed? 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. V S(1) S(1) S(2) y + _ 0x6 0xa a b c r x y Lifetimes Propagate Borrowed State fn f<'b, 'c>(x: &'b S, y: &'c S) -> &'c u8 { ... } let b = S(1); let c = S(2); let r = f(&b, &c); // We know returned reference is `c`-based, which must stay locked, // while `b` is free to move. let a = b; print_byte(r); * Liftime parameters in signatures, like 'c above, solve that problem. * Their primary purpose is: + outside the function, to explain based on which input address an output address could be generated, + within the function, to guarantee only addresses that live at least 'c are assigned. * The actual lifetimes 'b, 'c are transparently picked by the compiler at call site, based on the borrowed variables the developer gave. * They are not equal to the scope (which would be LOC from initialization to destruction) of b or c, but only a minimal subset of their scope called lifetime, that is, a minmal set of LOC based on how long b and c need to be borrowed to perform this call and use the obtained result. * In some cases, like if f had 'c: 'b instead, we still couldn't distinguish and both needed to stay locked. S(2) S(1) S(2) y + 1 0x6 0xa a b c r x y Unlocking let mut c = S(2); let r = f(&c); let s = r; // <- Not here, `s` prolongs locking of `c`. print_byte(s); let a = c; // <- But here, no more use of `r` or `s`. * A variable location is unlocked again once the last use of any reference that may point to it ends. |[?] Examples expand by clicking. Language Sugar^url 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 Weaken types to match signature, e.g., &mut T to &T. Deref ^NOM ^ Deref x: T until *x, **x, ... compatible with some target S. Prelude ^STD Automatic import of basic items, e.g., Option, drop, ... Reborrow Since x: &mut T can't be copied; move new &mut *x instead. Lifetime Elision ^BK Automatically annotate f(x: &T) to f<'a>(x: &'a ^NOM ^REF T). Method Resolution ^ Deref or borrow x until x.f() works. REF Match Ergonomics ^RFC Repeatedly dereference scrutinee and add ref and ref mut to bindings. Opinion ^ -- The features above will make your life easier, but might hinder your understanding. If any (type-related) operation ever feels inconsistent it might be worth revisiting this list. Types, Traits, Generics^url The building blocks of compile-time safety. u8 u16 f32 bool char Primitive Types File String Builder Composite Types Vec Vec Vec &'a T &'a T &'a T &mut 'a T &mut 'a T &mut 'a T [T; n] [T; n] [T; n] Type Constructors Vec Vec f() {} drop() {} Functions PI dbg! Other [?] Copy [?] Deref type Tgt; [?] From [?] From [?] From Traits Items defined in upstream crates. [?] Serialize [?] Transport [?] ShowHex Device [?] From Foreign trait impl. for local type. String [?] Serialize Local trait impl. for foreign type. String [?] From ^ Illegal, foreign trait for f. type. String [?] From Exception: Legal if used type local. Port [?] From [?] From Mult. impl. of trait with differing IN params. Container [?] Deref Tgt = u8; [?] Deref Tgt = f32; ^ Illegal impl. of trait with differing OUT params. T T T [?] ShowHex Blanket impl. of trait for any type. Your crate. A walk through the jungle of types, traits, and implementations that (might possibly) exist in your application. Type Paraphernalia^url Allowing users to bring their own types and avoid code duplication. (*) Types & Traits Types u8 String Device * Set of values with given semantics, layout, ... Type Values u8 { 0[u8], 1[u8], ..., 255[u8] } char { 'a', 'b', ... '' } struct S(u8, char) { (0[u8], 'a'), ... (255[u8], '') } Sample types and sample values. Type Equivalence and Conversions u8 &u8 &mut u8 [u8; 1] String * May be obvious but u8, &u8, &mut u8, 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 != 0 (in a mathematical sense) when it comes to types! In a language sense, the operation ==(0[u8], 0[u16]) just isn't defined to prevent happy little accidents. Type Values u8 { 0[u8], 1[u8], ..., 255[u8] } u16 { 0[u16], 1[u16], ..., 65_535[u16] } &u8 { 0xffaa[&u8], 0xffbb[&u8], ... } &mut u8 { 0xffaa[&mut u8], 0xffbb[&mut u8], ... } How values differ between types. * However, Rust might sometimes help to convert between types^1 + casts manually convert values of types, 0_i8 as u8 + coercions ^| automatically convert types if safe^2, let x: & u8 = &mut 0_u8; ^1 Casts 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 impl { ... } String impl { ... } Port impl { ... } impl Port { fn f() { ... } } * Types usually come with implementation, 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 { } [?] Copy [?] Clone [?] Sized [?] ShowHex * 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: Copy Trait Self u8 u16 ... Clone Trait Self u8 String ... Sized Trait Self char Port ... Traits as membership tables, Self refers to the type included. * Whoever is part of that membership list will adhere to behavior of list. * Traits can also include associated methods, functions, ... trait ShowHex { // Must be implemented according to documentation. fn as_hex() -> String; // Provided by trait author. fn print_hex() {} } [?] Copy trait Copy { } * Traits without methods often called marker traits. * Copy is example marker trait, meaning memory may be copied bitwise. [?] Sized * Some traits entirely outside explicit control * Sized provided by compiler for types with known size; either this is, or isn't Implementing Traits for Types -- impl T for S { } impl ShowHex for Port { ... } * Traits are implemented for types 'at some point'. * Implementation impl A for B add type B to the trait memebership list: ShowHex Trait Self Port * Visually, you can think of the type getting a "badge" for its membership: u8 impl { ... } [?] Sized [?] Clone [?] Copy Device impl { ... } [?] Transport Port impl { ... } [?] Sized [?] Clone [?] ShowHex Traits vs. Interfaces [?] Eat Venison [?] Eat venison.eat() Interfaces * In Java, Alice creates interface Eat. * When Bob authors Venison, he must decide if Venison implements Eat or not. * In other words, all membership must be exhaustively declared during type definition. * When using Venison, Santa can make use of behavior provided by Eat: // Santa imports `Venison` to create it, can `eat()` if he wants. import food.Venison; new Venison("rudolph").eat(); [?] Eat Venison / Venison + [?] Eat venison.eat() Traits * In Rust, Alice creates trait Eat. * Bob creates type Venison and decides not to implement Eat (he might not even know about Eat). * Someone^* later decides adding Eat to Venison would be a really good idea. * When using Venison Santa must import Eat separately: // Santa needs to import `Venison` to create it, and import `Eat` for trait method. use food::Venison; use tasks::Eat; // 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 Type Constructors -- Vec<> Vec Vec * Vec is type "vector of bytes"; Vec is type "vector of chars", but what is Vec<>? Construct Values Vec { [], [1], [1, 2, 3], ... } Vec { [], ['a'], ['x', 'y', 'z'], ... } 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 3^rd party to construct concrete type via parameter, + only then would this Vec become real type itself. Generic Parameters -- Vec [T; 128] &T &mut T S * Parameter for Vec<> often named T therefore Vec. * T "variable name for type" for user to plug in something specfic, Vec, S, ... Type Constructor Produces Family struct Vec {} Vec, Vec, Vec>, ... [T; 128] [u8; 128], [char; 128], [Port; 128] ... &T &u8, &u16, &str, ... Type vs type constructors. // S<> is type constructor with parameter T; user can supply any concrete type for T. struct S { x: T } // Within 'concrete' code an existing type must be given for T. fn f() { let x: S = S::new(0_f32); } Const Generics -- [T; N] and S [T; n] S * Some type constructors not only accept specific type, but also specific constant. * [T; n] constructs array type holding T type n times. * For custom types declared as MyArray. Type Constructor Produces Family [u8; N] [u8; 0], [u8; 1], [u8; 2], ... struct S {} S<1>, S<6>, S<123>, ... Type constructors based on constant. let x: [u8; 4]; // "array of 4 bytes" let y: [f32; 16]; // "array of 16 floats" // `MyArray` is type constructor requiring concrete type `T` and // concrete usize `N` to construct specific type. struct MyArray { data: [T; N], } Bounds (Simple) -- where T: X Num - Num Num Num u8 [?] Absolute [?] Dim [?] Mul Port [?] Clone [?] ShowHex * If T can be any type, how can we reason about (write code) for such a Num? * 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": // Type can only be constructed for some `T` if that // T is part of `Absolute` membership list. struct Num where T: Absolute { ... } Absolute Trait Self u8 u16 ... We add bounds to the struct here. In practice it's nicer add bounds to the respective impl blocks instead, see later this section. Bounds (Compound) -- where T: X + Y u8 [?] Absolute [?] Dim [?] Mul f32 [?] Absolute [?] Mul char Cmplx [?] Absolute [?] Dim [?] Mul [?] DirName [?] TwoD Car [?] DirName struct S where T: Absolute + Dim + Mul + DirName + TwoD { ... } * Long trait bounds can look intimidating. * In practice, each + X addition to a bound merely cuts down space of eligible types. Implementing Families -- impl<> When we write: impl S where T: Absolute + Dim + Mul { fn f(&self, x: T) { ... }; } It can be read as: * here is an implementation recipe for any type T (the impl part), * where that type must be member of the Absolute + Dim + Mul traits, * you may add an implementation block to S, * containing the methods ... You can think of such impl ... {} code as abstractly implementing a family of behaviors. Most notably, they allow 3^rd parties to transparently materialize implementations similarly to how type constructors materialize types: // If compiler encounters this, it will // - check `0` and `x` fulfill the membership requirements of `T` // - create two new version of `f`, one for `char`, another one for `u32`. // - based on "family implementation" provided s.f(0_u32); s.f('x'); Blanket Implementations -- impl X for T { ... } Can also write "family implementations" so they apply trait to many types: // Also implements Serialize for any type if that type already implements ToHex impl Serialize for T where T: ToHex { ... } These are called blanket implementations. ToHex Self Port Device ... - Whatever was in left table, may be added to right table, based on the following recipe (impl) - Serialize Trait Self u8 Port ... They can be neat way to give foreign types functionality in a modular way if they just implement another interface. ( ) Advanced Concepts^ Trait Parameters -- Trait { type Out; } Notice how some traits can be "attached" multiple times, but others just once? Port [?] From [?] From Port [?] Deref type u8; Why is that? * Traits themselves can be generic over two kinds of parameters: + trait From {} + trait Deref { type O; } * Remember we said traits are "membership lists" for types and called the list Self? * Turns out, parameters I (for input) and O (for output) are just more columns to that trait's list: impl From for u16 {} impl From for u32 {} impl Deref for Port { type O = u8; } impl Deref for String { type O = str; } From Self I u16 u8 u32 u16 ... Deref Self O Port u8 String str ... Input and output parameters. Now here's the twist, * any output O parameters must be uniquely determined by input parameters I, * (in the same way as a relation X Y would represent a function), * Self counts as an input. A more complex example: trait Complex { type O1; type O2; } * this creates a relation relation of types named Complex, * with 3 inputs (Self is always one) and 2 outputs, and it holds (Self, I1, I2) => (O1, O2) Complex Self [I] I1 I2 O1 O2 Player u8 char f32 f32 EvilMonster u16 str u8 u8 EvilMonster u16 String u8 u8 NiceMonster u16 String u8 u8 NiceMonster^^ u16 String u8 u16 Various trait implementations. The last one is not valid as (NiceMonster, u16, String) has already uniquely determined the outputs. Trait Authoring Considerations (Abstract) [?] A Car / Car [?] A car.a(0_u8) car.a(0_f32) [?] B type O; Car / Car [?] B T = u8; car.b(0_u8) car.b (0_f32) * 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 { } trait B { type O; } // Implementor adds (X, u32) to A. impl A for X { } // Implementor adds family impl. (X, ...) to A, user can materialze. impl A for Y { } // Implementor must decide specific entry (X, O) added to B. impl B for X { type O = u32; } A Self I X u32 Y ... Santa may add more members by providing his own type for T. B Self O Player String X u32 For given set of inputs (here Self), implementor must pre-select O. Trait Authoring Considerations (Example) [?] Audio - [?] Audio [?] Audio type O; [?] Audio type O; Choice of parameters goes along with purpose trait has to fill: No Additional Parameters trait Audio { fn play(&self, volume: f32); } impl Audio for MP3 { ... } impl Audio for Ogg { ... } mp3.play(0_f32); [?] Audio - MP3 [?] Audio Ogg [?] Audio Trait author assumes: * neither implementor nor user need to customize API. Input Parameters trait Audio { fn play(&self, volume: I); } impl Audio for MP3 { ... } impl Audio for MP3 { ... } impl Audio for MP3 { ... } impl Audio for Ogg where T: HeadsetControl { ... } mp3.play(0_f32); mp3.play(mixer); [?] Audio - MP3 [?] Audio [?] Audio [?] Audio Ogg [?] Audio ... where T is HeadsetCtrl. Trait author assumes: * developers would customize API in multiple ways for same Self type, * users (may want) ability to decide for which I-types ability should be possible. Output Parameters trait Audio { type O; fn play(&self, volume: Self::O); } impl Audio for MP3 { type O = f32; } impl Audio for Ogg { type O = Mixer; } mp3.play(0_f32); ogg.play(mixer); [?] Audio type O; - MP3 [?] Audio O = f32; Ogg [?] Audio O = Mixer; Trait author assumes: * developers 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! Multiple In- and Output Parameters trait Audio { type O; fn play(&self, volume: I) -> Self::O; } impl Audio for MP3 { type O = DigitalDevice; } impl Audio for MP3 { type O = AnalogDevice; } impl Audio for Ogg { type O = GenericDevice; } mp3.play(0_u8).flip_bits(); mp3.play(0_f32).rewind_tape(); [?] Audio type O; - MP3 [?] Audio O = DD; [?] Audio O = AD; Ogg [?] Audio O = GD; Like examples above, in particular trait author assumes: * users may want ability to decide for which I-types ability should be possible, * for given inputs, developer should determine resulting output type. ?Sized S - S S S struct S { ... } * T can be any concrete type. * However, there exists invisible default bound T: Sized, so S is not possible out of box. * Instead we have to add T : ?Sized to opt-out of that bound: S - S S S struct S where T: ?Sized { ... } Generics and Lifetimes -- <'a> S<'a> &'a f32 &'a mut u8 * Lifetimes act^* like type parameters: + user must provide specific 'a to instantiate type (compiler will help within methods), + as Vec and Vec are different types, so are S<'p> and S<'q>, + meaning you can't just assign value of type S<'a> to variable expecting S<'b> (exception: "subtype" relationship for lifetimes, e.g. 'a outliving 'b). S<'a> - S<'auto> S<'static> * 'static is only nameable instance of the typespace lifetimes. // `'a is free parameter here (user can pass any specific lifetime) struct S<'a> { x: &'a u32 } // In non-generic code, 'static is the only nameable lifetime we can explicitly put in here. let a: S<'static>; // Alternatively, in non-generic code we can (often must) omit 'a and have Rust determine // 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. ^ Note to self and TODO: that analogy seems somewhat flawed, as if S<'a> is to S<'static> like S is to S, then 'static would be a type; but then what's the value of that type? Examples expand by clicking. --------------------------------------------------------------------- Data Layout^url Memory representations of common data types. Basic Types^url Essential types built into the core of the language. Numeric Types ^REF^url u8, i8 u16, i16 u32, i32 u64, i64 u128, i128 f32 f64 usize, isize Same as ptr on platform. (*) Unsigned Types Type Max Value u8 255 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 usize Depending on platform pointer size, same as u16, u32, or u64. ( ) Signed Types Type Max Value i8 127 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 isize Depending on platform pointer size, same as i16, i32, or i64. Type Min Value 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 isize Depending on platform pointer size, same as i16, i32, or i64. ( ) Float Types^ Sample bit representation^* for a f32: 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: f32 S (1) E (8) F (23) Value Normalized number +- 1 to 254 any +-(1.F)[2] * 2^E-127 Denormalized number +- 0 non-zero +-(0.F)[2] * 2^-126 Zero +- 0 0 +-0 Infinity +- 255 0 +-[?] NaN +- 255 non-zero NaN Similarly, for f64 types this would look like: f64 S (1) E (11) F (52) Value Normalized number +- 1 to 2046 any +-(1.F)[2] * 2^E-1023 Denormalized number +- 0 non-zero +-(0.F)[2] * 2^-1022 Zero +- 0 0 +-0 Infinity +- 2047 0 +-[?] NaN +- 2047 non-zero NaN ^* Float types follow IEEE 754-2008 and depend on platform endianness. Textual Types ^REF^url char Any UTF-8 scalar. str ... U T F - 8 ... unspecified times Rarely seen alone, but as &str instead. (*) 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. let c = But not always. Given emoji is two char (see Encoding) '[?]'; and can't ^ be held by c.^1 c = Also, chars are not allowed ^ to hold arbitrary bit 0xffff_ffff; patterns. ^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 str is usually never held directly, but as &str, like s "a"; here. let s = It can hold arbitrary text, has variable length per c., "[?]"; and is hard to index. ( ) Encoding^ let s = "I Rust"; let t = "I [?] Rust"; Variant Memory Representation^2^ s.as_bytes 49 20 e2 9d a4 20 52 75 73 74 ^3^ () s.chars()^ 49 00 00 00 20 00 00 00 64 27 00 00 20 00 00 00 52 00 00 1^ 00 75 00 00 00 73 00 ... t.as_bytes 49 20 e2 9d a4 ef b8 8f 20 52 75 73 74 ^4^ () t.chars()^ 49 00 00 00 20 00 00 00 64 27 00 00 0f fe 01 00 20 00 00 1^ 00 52 00 00 00 75 00 ... ^1 Result then collected into array and transmuted to bytes. ^2 Values given in hex, on x86. ^3 Notice how , having Unicode Code Point (U+2764), is represented as 64 27 00 00 inside the char, but got UTF-8 encoded to e2 9d a4 in the str. ^4 Also observe how the emoji Red Heart [?], is a combination of and the U+FE0F Variation Selector, thus t has a higher char count than s. ^ 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^url Basic types definable by users. Actual layout ^REF is subject to representation; ^REF padding can be present. T x T Sized ^| T: ?Sized T Maybe DST ^| [T; n] T T T ... n times Fixed array of n elements. [T] ... T T T ... unspecified times Slice type of unknown-many elements. Neither Sized (nor carries len information), and most often lives behind reference as &[T]. ^| struct S; ; Zero-Sized ^| (A, B, C) A B C or maybe B A C Unless a representation is forced (e.g., via #[repr(C)]), type layout unspecified. struct S { b: B, c: C } B C or maybe C - B Compiler may also add padding. Also note, two types A(X, Y) and B(X, Y) with exactly the same fields can still have differing layout; never transmute() without representation guarantees. 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^url References give safe access to other memory, raw pointers unsafe access. The respective mut types are identical. &'a T ptr[2/4/8] meta[2/4/8] | T Must target some valid t of T, and any such target must exist for at least 'a. *const T ptr[2/4/8] meta[2/4/8] No guarantees. Pointer Meta^url 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. &'a T ptr[2/4/8] | T No meta for sized target. (pointer is thin). &'a T ptr[2/4/8] len[2/4/8] | T If T is a DST struct such as S { x: [u8] } meta field len is length of dyn. sized content. &'a [T] ptr[2/4/8] len[2/4/8] | ... T T ... Regular slice reference (i.e., the reference type of a slice type [T]) ^| often seen as &[T] if 'a elided. &'a str ptr[2/4/8] len[2/4/8] | ... U T F - 8 ... String slice reference (i.e., the reference type of string type str), with meta len being byte length. &'a dyn Trait ptr[2/4/8] ptr[2/4/8] | T | *Drop::drop(&mut T) size align *Trait::f(&T, ...) *Trait::g(&T, ...) Meta points to vtable, where *Drop::drop(), *Trait::f(), ... are pointers to their respective impl for T. Closures^url Ad-hoc functions with an automatically managed data block capturing ^ REF environment where closure was defined. For example: move |x| x + y.f() + z Y Z Anonymous closure type C1 |x| x + y.f() + z ptr[2/4/8] ptr[2/4/8] Anonymous closure type C2 | Y | Z Also produces anonymous fn such as f[c1](C1, X) or f[c2](&C2, X). Details depend which FnOnce, FnMut, Fn ... is supported, based on properties of captured types. Standard Library Types^url Rust's standard library combines the above primitive types into useful types with special semantics, e.g.: UnsafeCell T Magic type allowing aliased mutability. Cell T Allows T's to move in and out. RefCell borrowed T Also support dynamic borrowing of T. Like Cell this is Send, but not Sync. AtomicUsize usize[2/4/8] Other atomic similarly. Result Tag E or Tag T Option Tag or Tag T Tag may be omitted for certain T, e.g., NonNull. General Purpose Heap Storage^url Box ptr[2/4/8] meta[2/4/8] | T For some T stack proxy may carry meta^| (e.g., Box<[T]>). Vec ptr[2/4/8] capacity[2/4/8] len[2/4/8] | T T ... len - capacity - Owned Strings^url String ptr[2/4/8] capacity[2/4/8] len[2/4/8] | U T F - 8 ... len - capacity - Observe how String differs from &str and &[char]. CString ptr[2/4/8] len[2/4/8] | A B C ... len ... Nul-terminated but w/o nul in middle. OsString ^? Platform Defined | ? ? / ? ? Encapsulates how operating system represents strings (e.g., UTF-16 on Windows). PathBuf ^? OsString | ? ? / ? ? Encapsulates how operating system represents paths. Shared Ownership^url 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 ptr[2/4/8] meta[2/4/8] | strng[2/4/8] weak[2/4/8] T Share ownership of T in same thread. Needs nested Cell or RefCellto allow mutation. Is neither Send nor Sync. Arc ptr[2/4 /8] meta[2/4/8] | strng[2/4/8] weak[2/4/8] T Same, but allow sharing between threads IF contained T itself is Send and Sync. Mutex / RwLock ptr[2/4/8] poison[2/4/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). --------------------------------------------------------------------- Standard Library^url One-Liners^url Snippets that are common, but still easy to forget. See Rust Cookbook ^ for more. (*) Strings Intent Snippet Concatenate strings (any Display^| format!("{}{}", x, y) that is). ^1 Split by separator pattern. ^STD ^ s.split(pattern) ... with &str s.split("abc") ... with char s.split('/') ... with closure s.split(char::is_numeric) Split by whitespace. s.split_whitespace() Split by newlines. s.lines() Split by regular expression.^2 Regex::new(r"\s")?.split("one two three") ^1 Allocates; might not be fastest solution if x is String already. ^2 Requires regex crate. ( ) I/O Intent Snippet Create a new file File::create(PATH)? Same, via OpenOptions::new().create(true).write OpenOptions (true).truncate(true).open(PATH)? ( ) Macros Intent Snippet Macro w. variable arguments macro_rules! var_args { ($ ($args:expr),*) => {{ }} } Using args, e.g., calling f $( f($args); )* multiple times. ( ) Esoterics^ Intent Snippet Cleaner closure captures wants_closure({ let c = outer.clone(); move || use_clone(c) }) Fix inference in 'try' iter.try_for_each(|x| { Ok::<(), Error>(()) closures })?; Iterate and edit &mut [T] Cell::from_mut(mut_slice).as_slice_of_cells if T Copy. () Thread Safety^url Examples Send^* !Send Sync^* Most types ... Mutex, Arc MutexGuard^1, ^1,2 RwLockReadGuard^1 !Sync Cell^2, RefCell^2 Rc, &dyn Trait, *const T^3, *mut T^3 ^* An instance t where T: Send can be moved to another thread, a T: Sync means &t can be moved to another thread. ^1 If T is Sync. ^2 If T is Send. ^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. (Dynamically / Zero) Sized Types^url MostTypes [?] Sized Normal types. vs. Z [?] Sized Zero sized. vs. str [?] Sized Dynamically sized. [u8] [?] Sized dyn Trait [?] Sized ... [?] Sized (*) Overview * 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. ( ) Sized in Bounds Example Explanation struct A { x: u8 } Type A is sized, i.e., impl Sized for A holds, this is a 'regular' type. struct B { x: [u8] Since [u8] is a DST, B in turn becomes DST, i.e., } does not impl Sized. struct C { x: T Type params have implicit T: Sized bound, e.g., C } is valid, C is not. struct D Using ?Sized ^REF allows opt-out of that bound, { x: T } i.e., D is also valid. struct E; Type E is zero-sized (and also sized) and will not consume memory. trait F { fn f(& Traits do not have an implicit Sized bound, i.e., self); } impl F for B {} is valid. trait F: Sized Traits can however opt into Sized via {} supertraits.^| trait G { fn g For Self-like params DST impl may still fail as (self); } params can't go on stack. Iterators^url Collection [?] IntoIter Item = T; To = IntoIter Iterate over T. IntoIter [?] Iterator Item = T; &Collection [?] IntoIter Item = &T; To = Iter Iterate over &T. Iter [?] Iterator Item = &T; &mut Collectn [?] IntoIter Item = &mut T; To = IterMut Iterate over & mut T. IterMut [?] Iterator Item = &mut T; (*) Obtaining Iterators Basics Assume you have a collection c of type C: * c.into_iter() -- Turns collection c into an Iterator ^STD i and consumes^* c. Requires IntoIterator ^STD for C to be implemented. Type of item depends on what C was. 'Standardized' way to get Iterators. * c.iter() -- Courtesy method some collections provide, returns borrowing Iterator, doesn't consume c. * c.iter_mut() -- Same, but mutably borrowing Iterator that allow collection to be changed. The Iterator Once you have an i: * i.next() -- Returns Some(x) next element c provides, or None if we're done. For Loops * for x in c {} -- Syntactic sugar, calls c.into_iter() and loops i until None. ^* 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 the reference and turn it into an Iterator), but c remains untouched. ( ) Implementing Iterators Basics Let's assume you have a struct C {} that is your collection. * struct IntoIter {} -- Create a struct to hold your iteration status (e.g., an index) for value iteration. * impl Iterator for IntoIter {} -- Provide an implementation of Iterator::next() so it can produce elements. In addition, you might want to add a convenience C::iter(&self) -> IntoIter. Mutable Iterators * struct IterMut {} -- To provide mutable iterators create another struct that can hold C as &mut. * impl Iterator for IterMut {} -- In that case Iterator::Item is probably a &mut item Similarly, providing a C::iter_mut(&mut self) -> IterMut might be a good idea. Making Loops Work * impl IntoIterator for C {} -- Now for loops work as for x in c {}. * impl IntoIterator for &C {} -- For conveninece you might want to add these as well. * impl IntoIterator for &mut C {} -- Same ... Number Conversions^url As-correct-as-it-currently-gets number conversions. | Have / Want - u8 ... i128 f32 / f64 String u8 ... i128 u8::try_from(x)? ^1 x as f32 ^3 x.to_string() f32 / f64 x as u8 ^2 x as f32 x.to_string() String x.parse::()? x.parse::()? x ^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). Some mathematical pitfalls when dealing with numbers. (*) Casting Pitfalls ^ Cast^1 Gives Note 3.9_f32 as u8 3 Rounds towards zero, consider x.round() first. 314_f32 as u8 255 Takes closest available number. f32::INFINITY as 255 Same, treats INFINITY as really large u8 number. f32::NAN as u8 0 - _314 as u8 58 Truncates excess bits. _200 as i8 56 - _257 as i8 -1 - ( ) Arithmetical Pitfalls ^ Operation^1 Gives Note 200_u8 / 0_u8 Compile - error. 200_u8 / _0 ^d Panic. Regular math may panic; here: division by zero. 200_u8 / _0 ^r Panic. Same. 200_u8 + Compile - 200_u8 error. 200_u8 + _200 Panic. Consider checked_, wrapping_, ... ^d instead. ^STD 200_u8 + _200 144 In release mode this will overflow. ^r 0.8_f32 + 0.90000004 - 0.1_f32 1.0_f32 / f32::INFINITY - 0.0_f32 0.0_f32 / f32::NaN - 0.0_f32 ^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. In short, while all math will fail at domain limit, casts and floating point math won't panic; integer math may. String Conversions^url If you want a string of type ... (*) String If you have x of type ... Use this ... String x CString x.into_string()? OsString x.to_str()?.to_string() PathBuf x.to_str()?.to_string() Vec ^1 String::from_utf8(x)? &str x.to_string() ^i &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 If you have x of type ... Use this ... String CString::new(x)? CString x OsString ^2 CString::new(x.to_str()?)? PathBuf CString::new(x.to_str()?)? Vec ^1 CString::new(x)? &str CString::new(x)? &CStr x.to_owned() ^i &OsStr ^2 CString::new(x.to_os_string().into_string ()?)? &Path CString::new(x.to_str()?)? &[u8] ^1 CString::new(Vec::from(x))? *mut c_char ^3 unsafe { CString::from_raw(x) } ( ) OsString If you have x of type ... Use this ... String OsString::from(x) ^i CString OsString::from(x.to_str()?) OsString x PathBuf x.into_os_string() Vec ^1 ^? &str OsString::from(x) ^i &CStr OsString::from(x.to_str()?) &OsStr OsString::from(x) ^i &Path x.as_os_str().to_owned() &[u8] ^1 ^? ( ) PathBuf If you have x of type ... Use this ... String PathBuf::from(x) ^i CString PathBuf::from(x.to_str()?) OsString PathBuf::from(x) ^i PathBuf x Vec ^1 ^? &str PathBuf::from(x) ^i &CStr PathBuf::from(x.to_str()?) &OsStr PathBuf::from(x) ^i &Path PathBuf::from(x) ^i &[u8] ^1 ^? ( ) Vec If you have x of type ... Use this ... String x.into_bytes() CString x.into_bytes() OsString ^? PathBuf ^? Vec ^1 x &str Vec::from(x.as_bytes()) &CStr Vec::from(x.to_bytes_with_nul()) &OsStr ^? &Path ^? &[u8] ^1 x.to_vec() ( ) &str If you have x of type ... Use this ... String x.as_str() CString x.to_str()? OsString x.to_str()? PathBuf x.to_str()? Vec ^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 If you have x of type ... Use this ... String CString::new(x)?.as_c_str() CString x.as_c_str() OsString ^2 x.to_str()? PathBuf ^?^,4 Vec ^1^,5 CStr::from_bytes_with_nul(&x)? &str ^?^,4 &CStr x &OsStr ^2 ^? &Path ^? &[u8] ^1^,5 CStr::from_bytes_with_nul(x)? *const c_char ^1 unsafe { CStr::from_ptr(x) } ( ) &OsStr If you have x of type ... Use this ... String OsStr::new(&x) CString ^? OsString x.as_os_str() PathBuf x.as_os_str() Vec ^1 ^? &str OsStr::new(x) &CStr ^? &OsStr x &Path x.as_os_str() &[u8] ^1 ^? ( ) &Path If you have x of type ... Use this ... String Path::new(x) ^r CString Path::new(x.to_str()?) OsString Path::new(x.to_str()?) ^r PathBuf Path::new(x.to_str()?) ^r Vec ^1 ^? &str Path::new(x) ^r &CStr Path::new(x.to_str()?) &OsStr Path::new(x) ^r &Path x &[u8] ^1 ^? ( ) &[u8] If you have x of type ... Use this ... String x.as_bytes() CString x.as_bytes() OsString ^? PathBuf ^? Vec ^1 &x &str x.as_bytes() &CStr x.to_bytes_with_nul() &OsStr x.as_bytes() ^2 &Path ^? &[u8] ^1 x ( ) Other You want And have x Use this ... *const c_char CString x.as_ptr() ^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::::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. ^5 Must ensure vector actually ends with 0x0. String Output^url 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: Macro Output Notes format!(fmt) String Bread-and-butter "to String" converter. print!(fmt) Console Writes to standard output. println!(fmt) Console Writes to standard output. eprint!(fmt) Console Writes to standard error. eprintln!(fmt) Console Writes to standard error. write!(dst, fmt) Buffer Don't forget to also use std::io::Write; writeln!(dst, fmt) Buffer Don't forget to also use std::io::Write; 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 In format! and friends, types convert via trait Display "{}" ^STD or Debug "{:?}" ^STD , non exhaustive list: Type Implements String Debug, Display CString Debug OsString Debug PathBuf Debug Vec Debug &str Debug, Display &CStr Debug &OsStr Debug &Path Debug &[u8] Debug bool Debug, Display char Debug, Display u8 ... i128 Debug, Display f32, f64 Debug, Display ! Debug, Display () Debug In short, pretty much everything is Debug; more special types might need special handling or conversion ^| to Display. ( ) 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, ...) or argument name, e.g., print!("{x}", x = 3). 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. Format Explanation Example {} Print the next argument using Display.^STD {:?} Print the next argument using Debug.^STD {2:#?} Pretty-print the 3^rd argument with Debug^STD formatting. {val:^2$} Center the val named argument, width specified by the 3^ rd 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). Full Example Explanation println!("{}", x) Print x using Display^STD on std. out and append new line. format!("{a:.3} {b:?}", Convert PI with 3 digits, add space, b with a = PI, b = 2) Debug ^STD, return String. --------------------------------------------------------------------- Tooling^url Project Anatomy^url 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. Individual examples are run like cargo run --example my_example.rs my_example. src/ Actual source code for your project. main.rs Default entry point for applications, this is what cargo run uses. 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. .rustfmt.toml In case you want to customize how cargo fmt works. .clippy.toml Special configuration for certain clippy lints, utilized via cargo clippy ^ build.rs Pre-build script, ^ useful when compiling C / FFI, ... Cargo.toml Main project manifest, ^ Defines dependencies, artifacts ... Cargo.lock Dependency details for reproducible builds, recommended to git for apps, not for libs. ^* On stable consider Criterion. Minimal examples for various entry points might look like: (*) Applications // src/main.rs (default application entry point) fn main() { println!("Hello, world!"); } ( ) Libraries // src/lib.rs (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. ( ) Unit Tests // src/my_module.rs (any file of your project) fn f() -> u32 { 0 } #[cfg(test)] mod test { use super::f; // Need to import items from parent module. Has // access to non-public members. #[test] fn ff() { assert_eq!(f(), 0); } } ( ) Integration Tests // tests/sample.rs (sample integration test) #[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. ( ) Benchmarks // benches/sample.rs (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}; #[bench] fn my_algo(b: &mut Bencher) { b.iter(|| black_box(my_crate::f())); // `black_box` prevents `f` from being optimized away. } ( ) Build Scripts // build.rs (sample pre-build script) fn main() { // 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^ // src/lib.rs (default entry point for proc macros) extern crate proc_macro; // Apparently needed to be imported like this. use proc_macro::TokenStream; #[proc_macro_attribute] // Can now be used as `#[my_attribute]` pub fn my_attribute(_attr: TokenStream, item: TokenStream) -> TokenStream { item } // Cargo.toml [package] name = "my_crate" version = "0.1.0" [lib] proc-macro = true Module trees and imports: (*) Module Trees Modules ^BK ^EX ^REF and source files work as follows: * 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). Actual module definitions work as follows: * 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^ Rust has three kinds of namespaces: Namespace Types Namespace Functions Namespace Macros mod X {} fn X() {} macro_rules! X { ... } X (crate) const X: u8 = 1; trait X {} static X: u8 = 1; enum X {} union X {} struct X {} struct X;^1 struct X();^1 ^1 Counts in Types and in Functions. * 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^url Commands and tools that are good to know. Command Description cargo init Create a new project for the latest edition. cargo build Build the project in debug mode (--release for all optimization). cargo check Check if project would compile (much faster). cargo test Run tests for the project. cargo run Run your project, if a binary is produced (main.rs). cargo run --bin Run binary b. Unifies features with other b dependents (can be confusing). cargo run -p w Run main of sub-workspace w. Treats features more as you would expect. cargo tree Show dependency graph. cargo doc --open Locally generate documentation for your code and dependencies. cargo +{nightly, Use given toolchain for command, e.g., for stable} ... 'nightly only' tools. cargo +nightly ... Some nightly-only commands (substitute ... with command below) build -Z timings Show what crates caused your build to take so long, highly useful. ^ ^ rustc -- Show expanded macros. ^ -Zunpretty=expanded rustup doc Open 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]. Tool Description cargo Additional (lints) catching common API misuses and clippy unidiomatic code. ^ cargo fmt Automatic code formatter (rustup component add rustfmt). ^ A large number of additional cargo plugins can be found here. Cross Compilation^url Check target is supported. Install target via rustup target install X. Install native toolchain (required to link, depends on target). Get from target vendor (Google, Apple, ...), might not be available on all hosts (e.g., no iOS toolchain on Windows). Some toolchains require additional build steps (e.g., Android's make-standalone-toolchain.sh). Update ~/.cargo/config.toml like this: [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 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. [?] Compile with cargo build --target=X Tooling Directives^url Special tokens embedded in source code used by tooling or preprocessing. (*) Macros Inside a declarative ^BK macro by example ^BK ^EX ^REF macro_rules! implementation these work: Within Macros Explanation $x:ty Macro capture (here a type). $x:item An item, like a function, struct, module, etc. $x:block A block {} of statements or expressions, e.g., { let x = 5; } $x:stmt A statement, e.g., let x = 1 + 1;, String::new(); or vec![]; $x:expr An expression, e.g., x, 1 + 1, String::new() or vec![] $x:pat A pattern, e.g., Some(t), (17, 'a') or _. $x:ty A type, e.g., String, usize or Vec. $x:ident An identifier, for example in let x = 0; the identifier is x. $x:path A path (e.g. foo, ::std::mem::replace, transmute::<_, int>). A literal (e.g. 3, "foo", b"bar", etc.). $x:literal A lifetime (e.g. 'a, 'static, etc.). $x:lifetime $x:meta A meta item; the things that go inside #[...] and #! [...] attributes. $x:vis A visibility modifier; pub, pub(crate), etc. $x:tt A single token tree, see here for more details. $crate Special hygiene variable, crate where macros is defined. ^? ( ) Documentation Inside a doc comment ^BK ^EX ^REF these work: Within Doc Explanation Comments ```...``` Include a doc test (doc code running on cargo test). ```X,Y ...``` Same, and include optional configurations; with X, Y being ... rust Make it explicit test is written in Rust; implied by Rust tooling. - Compile test. Run test. Fail if panic. Default behavior. Compile test. Run test. Execution should panic. If should_panic not, fail test. no_run Compile test. Fail test if code can't be compiled, Don't run test. Compile test but fail test if code can be compiled. compile_fail ignore Do not compile. Do not run. Prefer option above instead. Execute code as Rust '18; default is '15. edition2018 # Hide line from documentation (``` # use x::hidden; ```). [`S`] Create a link to struct, enum, trait, function, ... S. [`S`](crate::S) Paths can also be used, in the form of markdown links. ( ) #![globals] Attributes affecting the whole crate or app: Opt-Out's On Explanation #![no_std] C Don't (automatically) import std^STD ; use core^STD instead. ^REF #! CM Don't add prelude^STD, need to manually [no_implicit_prelude] 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, C Rely on features that may never get stabilized, c. b, c)] Unstable Book. ^ Builds On Explanation #![windows_subsystem C On Windows, make a console or windows app. ^ = "x"] REF ^ #![crate_name = "x"] C Specifiy current crate name, e.g., when not using cargo. ^? ^REF ^ #![crate_type = C Specifiy current crate type (bin, lib, dylib, "bin"] cdylib, ...). ^REF ^ #![recursion_limit = C Set compile-time recursion limit for deref, "123"] macros, ... ^REF ^ #![type_length_limit C Limits maximum number of type substitutions. = "456"] ^REF ^ Handlers On Explanation #[panic_handler] F Make some fn f(&PanicInfo) -> ! app's panic handler. ^REF # S Make static item impl. GlobalAlloc ^STD global [global_allocator] allocator. ^REF ( ) #[code] Attributes primarily governing emitted code: Developer UX On Explanation # T Future-proof struct or enum; hint it may grow in [non_exhaustive] future. ^REF #[path = "x.rs"] M Get module from non-standard file. ^REF Codegen On Explanation #[inline] F Nicely suggest compiler should inline function at call sites. ^REF #[inline(always)] F Emphatically threaten compiler to inline call, or else. ^REF #[inline(never)] F Instruct compiler to feel disappointed if it still inlines the function. ^REF #[cold] F Hint that function probably isn't going to be called. ^REF #[target_feature F Enable CPU feature (e.g., avx2) for code of (enable="x")] unsafe fn. ^REF #[track_caller] F Allows fn to find caller^STD for better panic messages. ^REF #[repr(X)]^1 T Use another representation instead of the default rust ^REF one: #[repr(C)] T Use a C-compatible (f. FFI), predictable (f. transmute) layout. ^REF #[repr(C, enum Give enum discriminant the specified type. ^ u8)] REF #[repr T Give single-element type same layout as (transparent)] contained field. ^REF #[repr T Lower alignment of struct and contained (packed(1))] fields, mildly UB prone. ^REF #[repr(align T Raise alignment of struct to given value, (8))] e.g., for SIMD types. ^REF ^1 Some representation modifiers can be combined, e.g., #[repr(C, packed(1))]. Linking On Explanation #[no_mangle] * Use item name directly as symbol name, instead of mangling. ^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 kind="y")] symbol. ^REF #[link_name = F Name of symbol to search for resolving extern "foo"] fn. ^REF #[link_section = FS Section name of object file where item should ".sample"] be placed. ^REF #[export_name = FS Export a fn or static under a different name. "foo"] ^REF #[used] S Don't optimize away static variable despite it looking unused. ^REF ( ) #[quality] Attributes used by Rust tools to improve code quality: Code Patterns On Explanation #[allow(X)] * Instruct rustc / clippy to ... ignore class X of possible issues. ^REF #[warn(X)] ^1 * ... emit a warning, mixes well with clippy lints. ^ ^REF #[deny(X)] ^1 * ... fail compilation. ^REF #[forbid(X)] ^1 * ... fail compilation and prevent subsequent allow overrides. ^REF #[deprecated = * Let your users know you made a design mistake. ^ "msg"] REF #[must_use = FTX Makes compiler check return value is processed by "msg"] caller. ^ ^REF ^1 There is some debate which one is the best to ensure high quality crates. Actively maintained multi-dev crates probably benefit from more aggressive deny or forbid lints; less-regularly updated ones probably more from conservative use of warn (as future compiler or clippy updates may suddenly break otherwise working code with minor issues). Tests On Explanation #[test] F Marks the function as a test, run with cargo test. ^ ^REF #[ignore = F Compiles but does not execute some #[test] for now. "msg"] ^REF # F Test must panic!() to actually succeed. ^REF [should_panic] #[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 CM ... from cleaning up attribute x. ^ (x)] Documentation On Explanation #[doc = "Explanation"] * Same as adding a /// doc comment. ^ #[doc(alias = "other")] * Provide another name users can search for in the docs. ^ #[doc(hidden)] * Prevent item from showing up in docs. ^ #![doc(html_favicon_url C Sets the favicon for the docs. ^ = "")] #![doc(html_logo_url = C The logo used in the docs. ^ "")] #![doc Generates Run buttons and uses given (html_playground_url = C service. ^ "")] #![doc(html_root_url = C Base URL for links to external crates. ^ "")] #![doc(html_no_source)] C Prevents source from being included in docs. ^ ( ) #[macros] Attributes related to the creation and use of macros: Macros By On Explanation Example # ! Export macro_rules! as pub on crate level ^REF [macro_export] #[macro_use] MX Let macros persist past modules; or import from extern crate. ^REF Proc Macros On Explanation #[proc_macro] F Mark fn as function-like procedural macro callable as m!(). ^REF #[proc_macro_derive F Mark fn as derive macro which can #[derive (Foo)] (Foo)]. ^REF # F Mark fn as attribute macro which can [proc_macro_attribute] understand new #[x]. ^REF Derives On Explanation #[derive T Let some proc macro provide a goodish impl of trait X. (X)] ^ ^REF ( ) #[cfg] Attributes governing conditional compilation: Config Attributes On Explanation #[cfg(X)] * Include item if configuration X holds. ^REF #[cfg(all(X, Y, Z))] * Include item if all options hold. ^REF #[cfg(any(X, Y, Z))] * Include item if at least one option holds. ^REF #[cfg(not(X))] * Opposite day. ^REF #[cfg_attr(X, foo = * Apply #[foo = "msg"] if configuration X "msg")] holds. ^REF [?][?] 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. Known Options On Explanation #[cfg(target_arch = * The CPU architecture crate is compiled "x86_64")] for. ^REF #[cfg(target_feature = * Whether a particular class of "avx")] instructions is available. ^REF #[cfg(target_os = * Operating system your code will run on. ^ "macos")] REF #[cfg(target_family = * Family operating system belongs to. ^REF "unix")] #[cfg(target_env = * How DLLs and functions are interfaced "msvc")] with on OS. ^REF #[cfg(target_endian = * Main reason your cool new zero-cost "little")] protocol fails. ^REF #[cfg How many bits pointers, usize and CPU (target_pointer_width = * words have. ^REF "64")] #[cfg(target_vendor = * Manufacturer of target. ^REF "apple")] #[cfg(debug_assertions)] * Whether debug_assert!() and friends would panic. ^REF #[cfg(proc_macro)] * Wheter crate compiled as proc macro. ^REF #[cfg(test)] * Whether compiled with cargo test. ^ ^REF #[cfg(feature = * When your crate was compiled with feature "serde")] serde. ^ ^REF ( ) build.rs Environment variables and outputs related to the pre-build script. Input Environment Explanation ^REF CARGO_FEATURE_X Environment variable set for each feature x activated. CARGO_FEATURE_SERDE If feature serde were enabled. If feature some-feature were enabled; dash CARGO_FEATURE_SOME_FEATURE - converted to _. CARGO_CFG_X Exposes cfg's; joins mult. opts. by , and converts - to _. CARGO_CFG_TARGET_OS= If target_os were set to macos. macos If target_feature were set to avx and CARGO_CFG_TARGET_FEATURE= avx2. avx,avx2 OUT_DIR Where output should be placed. TARGET Target triple being compiled for. HOST Host triple (running this build script). Available in build.rs via env!(). List not exhaustive. Output String Explanation ^REF cargo:rerun-if-changed=PATH (Only) run this build.rs again if PATH changed. cargo:rerun-if-env-changed= (Only) run this build.rs again if VAR environment VAR changed. cargo:rustc-link-lib=[KIND Link native library as if via -l option. =]NAME cargo:rustc-link-search= Search path for native library as if via [KIND=]PATH -L option. cargo:rustc-flags=FLAGS Add special flags to compiler. ^? cargo:rustc-cfg=KEY[= Emit given cfg option to be used for "VALUE"] later compilation. cargo:rustc-env=VAR=VALUE Emit var accessible via env!() in crate during compilation. cargo:rustc-cdylib-link-arg When building a cdylib, pass linker flag. =FLAG cargo:warning=MESSAGE Emit compiler warning. Emitted from build.rs via println!(). List not exhaustive. For the On column in attributes: C means on crate level (usually given as #![my_attr] in the top level file). M means on modules. F means on functions. S means on static. T means on types. X means something special. ! means on macros. * means on almost any item. --------------------------------------------------------------------- Coding Guides^url Idiomatic Rust^url If you are used to programming Java or C, consider these. Idiom Code Think in x = if x { a } else { b }; Expressions x = loop { break 5 }; fn f() -> u32 { 0 } Think in (1..10).map(f).collect() Iterators names.iter().filter(|x| x.starts_with("A")) Handle Absence x = try_something()?; with ? get_option()?.run()? Use Strong enum E { Invalid, Valid { ... } } over ERROR_INVALID Types = -1 enum E { Visible, Hidden } over visible: bool struct Charge(f32) over f32 Provide Car::new("Model T").hp(20).build(); Builders Split Generic types S can have a separate impl per T. Implementations Rust doesn't have OO, but with separate impl you can get specialization. Unsafe Avoid unsafe {}, often safer, faster solution without it. Exception: FFI. Implement #[derive(Debug, Copy, ...)] and custom impl where Traits needed. Tooling With 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. 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. 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^url 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. ^STD async fn f Function f returns an impl Future. () {} async fn f Function f returns an impl Future. () -> S {} async { x } Transforms { x } into an impl Future. let sm = f(); Calling f() that is async will not execute f, but produce state machine sm. ^1 ^2 sm = async Likewise, does not execute the { g() } block; { g() }; produces state machine. runtime.block_on Outside an async {}, schedules sm to actually run. (sm); Would execute g(). ^3 ^4 sm.await Inside an async {}, run sm until complete. Yield to runtime if sm not 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., async-std or tokio 0.2+. 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: * 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 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. ( ) 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. set_TL(a); Definitely bad ^, await may return from other x.await; TL(); thread, thread local invalid. s.no(); x.await; Maybe bad ^, await will not return if Future s.go(); dropped while waiting. ^2 Rc::new(); Non-Send types prevent impl Future from being Send; x.await; rc(); 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. Closures in APIs^url 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 From a call site perspective that means: Signature Function g can call ... Function g accepts ... g(f: F) ... f() once. Fn, FnMut, FnOnce g(mut f: F) ... f() multiple times. Fn, FnMut g(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: Closure Implements^* Comment || { moved_s; FnOnce Caller must give up ownership of } moved_s. || { &mut s; FnOnce, FnMut Allows g() to change caller's local } state s. || { &s; } FnOnce, FnMut, May not mutate state; but can share and Fn 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: Requiring Advantage Disadvantage F: FnOnce Easy to satisfy as Single use only, g() may call f() caller. just once. F: FnMut Allows g() to change Caller may not reuse captures caller state. during g(). F: Fn Many can exist at same Hardest to produce for caller. time. Unsafe, Unsound, Undefined^url Unsafe leads to unsound. Unsound leads to undefined. Undefined leads to the dark side of the force. (*) 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. // `x` must always point to race-free, valid, aligned, initialized u8 memory. unsafe fn unsafe_f(x: *mut u8) { my_native_lib(x); } ( ) Undefined Behavior 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 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 should_be_true() { let r: &u8 = unsafe { &*ptr::null() }; // Once this runs, ENTIRE app is undefined. Even if } else { // line seemingly didn't do anything, app might now run println!("the spanish inquisition"); // 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. fn unsound_ref(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 small, sound modules that are easy to review. + Never create unsound abstractions; if you can't encapsulate unsafe properly, don't do it. + Each unsafe unit should be accompanied by plain-text reasoning outlining its safety. API Stability^url When updating an API, these changes can break client code.^RFC Major changes () are definitely breaking, while minor changes () might be breaking: Crates Making a crate that previously compiled for stable require nightly. Altering use of Cargo features (e.g., adding or removing features). 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. Adding public field when no private field exists. Adding or removing private fields when at least one already exists (before and after the change). 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 Adding new fields to a variant. 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. Adding a defaulted type parameter. 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. Signatures in Type Definitions Tightening bounds (e.g., to ). Loosening bounds. Adding defaulted type parameters. Generalizing to generics. Signatures in Functions Adding / removing arguments. Introducing a new type parameter. Generalizing to generics. Behavioral Changes / Changing semantics might not cause compiler errors, but might make clients do wrong thing. --------------------------------------------------------------------- Misc^url Links & Services^url These are other great guides and tables. Cheat Sheets Description Rust Learning Probably the best collection of links about learning Rust. Functional Jargon in A collection of functional programming jargon Rust explained in Rust. Periodic Table of How various types and references correlate. Types Futures How to construct and work with futures. Rust Iterator Cheat Summary of iterator-related methods from Sheet std::iter and itertools. Type-Based Rust Lists common types and how they convert. Cheat Sheet All major Rust books developed by the community. Books [?] Description The Rust Programming Standard introduction to Rust, start here if Language you are new. API Guidelines How to write idiomatic and re-usable Rust. Asynchronous Explains async code, Futures, ... Programming ^ Design Patterns Idioms, Patterns, Anti-Patterns. Edition Guide Working with Rust 2015, Rust 2018, and beyond. Guide to Rustc Explains how the compiler works internally. Development Little Book of Rust Community's collective knowledge of Rust Macros ^ macros. Reference ^ Reference of the Rust language. RFC Book Look up accepted RFCs and how they change the language. Performance Book Techniques to improve the speed and memory usage. Rust Cookbook Collection of simple examples that demonstrate good practices. Rust in Easy Explains concepts in simplified English, English good alternative start. Rustdoc Book Tips how to customize cargo doc and rustdoc. Rustonomicon Dark Arts of Advanced and Unsafe Rust Programming. Unsafe Code Concise information about writing unsafe Guidelines ^ code. Unstable Book Information about unstable items, e.g, #! [feature(...)]. The Cargo Book How to use cargo and write Cargo.toml. The CLI Book Information about creating CLI tools. The Embedded Book Working with embedded and #![no_std] devices. The Embedonomicon First #![no_std] from scratch on a Cortex-M. The WebAssembly Book Working with the web and producing .wasm files. The wasm-bindgen How to bind Rust and JavaScript APIs in Guide particular. For more inofficial books see Little Book of Rust Books. Comprehensive lookup tables for common components. Tables Description Rust Changelog See all the things that changed in a particular version. Rust Forge Lists release train and links for people working on the compiler. Rust Platform All supported platforms and their Tier. Support Rust Component Check nightly status of various Rust tools for a History platform. ALL the Clippy Lints All the clippy lints you might be interested in. Configuring Rustfmt All rustfmt options you can use in .rustfmt.toml. Compiler Error Index Ever wondered what E0404 means? Online services which provide information or tooling. Services [?][?] Description crates.io All 3^rd party libraries for Rust. std.rs Shortcut to std documentation. docs.rs Documentation for 3^rd party libraries, automatically generated from source. lib.rs Unofficial overview of quality Rust libraries and applications. caniuse.rs Check which Rust version introduced or stabilized a feature. Rust Playground Try and share snippets of Rust code. Rust Search Browser extension to search docs, crates, attributes, Extension books, ... Printing & PDF^url Want this Rust cheat sheet as a PDF? Download the latest PDF here . Alternatively, generate it yourself via File > Print and then "Save as PDF" (works great in Chrome, has some issues in Firefox). Ralf Biedert, 2021 - cheats.rs Legal & Privacy