Fork me on GitHub

Rust 语言速查 Snugly fits the Bayeux tapestry.

Rust 权威指南,BK Rust 实例教程,EX Rust 标准库文档,STD Rust 死灵书,NOM 以及 Rust 参考手册.REF

点击跳转

BK Rust 权威指南.
EX Rust 实例教程.
STD Rust 标准库 (API).
NOM Rust 死灵书.
REF Rust 参考手册.
RFC 官方 RFC 文档.
🔗 网络资源.
本页面, 上面.
本页面, 下面.

其他

🗑️ 弃用 (deprecated).
'18 最低 Rust 版本要求.
🚧 需要 Rust nightly (或者还未完整实现).
🛑 故意的错误示例隐患.
🧠 略显深奥, 少见的或高级的用法.
🔥 相当实用的东西.
↪  展开为 …
💬 个人看法.
? 缺失链接或解释.

连字 (..=, =>) 手动切换主题 💡 主题跟随系统 💡 X-Ray 📈
X-Ray visualizations are enabled. These show aggregated feedback per section. Right now this is experimental. Known caveats:
  • some sections that receive lots of feedback aren't shown (e.g., "Hello Rust")
  • it does not account for age of the section (some sections have been around for many years, others for just a few months)
  • it does neither account for feedback bursts (people mashing the same button 3 times), nor "most recent feedback" (people up- then down-voting).
The feedback format is (positive, negative, textual), equivalent to use of the feedback buttons.

Language Constructs | 语言结构

Behind the Scenes | Rust 幕后那些事

Memory Layout | 内存布局

Misc | 杂项

Standard Library | 标准库

Tooling | 工具

Working with Types | 妙用类型系统

Coding Guides | 代码指引

你好, Rust!url 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

If you are new to Rust, or if you want to try the things below:

如果你刚刚接触 Rust, 或者你想试试下面的东西:

fn main() {
    println!("Hello, world!");
}
Service provided by play.rust-lang.org 🔗

Things Rust does measurably really well

Rust 在这些方面表现非常好

Points you might run into

你可能遇到的问题

  • Steep learning curve;1 compiler enforcing (esp. memory) rules that would be "best practices" elsewhere.

    陡峭的学习曲线;1 编译器强制执行在其他语言中可能只是 "最佳实践" 的 (特别是内存方面上的) 规则.

  • Missing Rust-native libs in some domains, target platforms (esp. embedded), IDE features.1

    在某些领域、目标平台 (特别是嵌入式) 和 IDE 功能方面缺乏 Rust 原生库.1

  • Longer compile times than "similar" code in other languages.1

    与其他语言写成的 "类似" 的代码相比, 编译时间更长.1

  • Careless (use of unsafe in) libraries can secretly break safety guarantees.

    不谨慎 (使用 unsafe 的) 库可能会悄悄破坏安全保证.

  • No formal language specification, 🔗 can prevent legal use in some domains (aviation, medical, …). 🔗

    没有正式的语言规范 🔗 可能会阻止在某些领域 (航空、医疗等) 的合法使用. 🔗

  • Rust Foundation may offensively use their IP to affect 'Rust' projects (e.g, forbid names, impose policies). 🔗🔗2

    Rust 基金会可能会积极维护其知识产权, 影响到 'Rust' 项目(例如, 禁止名称, 强制执行政策). 🔗🔗

1 Compare Rust Survey.
2 Avoiding their marks (e.g, in your name, URL, logo, dress) is probably sufficient. 避免使用他们的商标 (例如在你的名称、URL、标志、外观中) 可能就足够了.

Download

IDEs

Modular Beginner Resources

模块化的初学者资源

In addition consider The Book,BK Rust by Example,EX the Standard Library,STD and Learn Rust.🔗

再看看: Rust 权威指南,BK Rust 实例教程,EX Rust 标准库,STD and Learn Rust.🔗

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.

个人看法 💬 — 如果您从未见过或使用过 Rust, 那么在继续阅读之前最好先访问一下上面的链接之一; 否则下一章你可能会感觉内容有点简洁.

Data Structuresurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Data types and memory locations defined via keywords.

按关键字列出的数据类型和内存位置信息.

Example 示例Explanation 解释
struct S {}Define a struct BK EX STD REF with named fields.
定义一个 结构体 (struct) BK EX STD REF, 内含一些字段 (field)
     struct S { x: T }Define struct with named field x of type T.
定义一个带有字段 x 的结构体 T
     struct S ​(T);Define "tupled" struct with numbered field .0 of type T.
定义一个 "元组" 结构体 T, 内部字段是以数字顺序命名的
     struct S;Define zero sized NOM unit struct. Occupies no space, optimized away.
定义一个 零大小 NOM 的单元结构体 (ZST). 不占任何空间, 会被优化掉.
enum E {}Define an enum, BK EX REF c. algebraic data types, tagged unions.
定义一个 枚举 (enum), BK EX REF, 参见 代数数据类型 (algebraic data types, ADT), 标记联合 (tagged unions).
     enum E { A, B(), C {} }Define variants of enum; can be unit- A, tuple- B ​() and struct-like C{}.
定义了一个 enum; 其变体 (variant) 包括单元类型 A, 元组类型 B ​() 和类似结构体的 C{}.
     enum E { A = 1 }Enum with explicit discriminant values, REF e.g., for FFI.
带有显式 辨别值 的 enum, REF 例如, 用于 FFI.
     enum E {}Enum w/o variants is uninhabited, REF can't be created, c. 'never' 🧠
枚举没有变体则不可实例化 (uninhabited), REF 参见 'never' 🧠
union U {}Unsafe C-like union REF for FFI compatibility. 🧠
不安全的类似 C 里面的 联合 (union) REF, 用以实现 FFI 兼容性. 🧠
static X: T = T();Global variable BK EX REF with 'static lifetime, single 🛑1 memory location.
全局变量 BK EX REF, 具有 'static 的生命周期, 单一 🛑1 内存位置.
const X: T = T();Defines constant, BK EX REF copied into a temporary when used.
定义一个 常量, BK EX REF 哪里需要就复制到哪里 (译者注: 注意区分于全局变量, 区分于 static 关键字).
let x: T;Allocate T bytes on stack2 bound as x. Assignable once, not mutable.
在栈上2分配 T 字节 (按 T 的大小分配对应字节大小的栈内存), 绑定为 x (命名为 x). 只能赋值一次, 不可变.
let mut x: T;Like let, but allow for mutability BK EX and mutable borrow.3
类似 let, 但可变 BK EX, 允许可变借用.3
     x = y;Moves y to x, inval. y if T is not Copy, STD and copying y otherwise.
y 移动到 x. 如果 T 不是 Copy 的 (译者注: 语法层面上表现为未实现 Copy 这个 trait, 两种说法一个意思), STD 则使 y 无效 (译者注: 即 y 被 "移动" 了, 原来的地方就没有了), 否则复制 y (译者注: 即相当于移动了 y 的副本, 因为 Copy 是栈操作, 非常快, 开销可以忽略).

1 In libraries you might secretly end up with multiple instances of X, depending on how your crate is imported. 🔗 作为 , 你可能会间接地最终得到多个 X 的实例, 这取决于你的 crate 是如何被导入的. 🔗
2 Bound variables BK EX REF live on stack for synchronous code. In async {} they become part of async's state machine, may reside on heap. 有界变量 (译者注: 这里翻译有待商榷. 可能指有界变量, 即变量的类型是有确切大小的, 区分于动态大小类型 DST; 也可能指已绑定的变量, 但原文指出使用 let 关键字在栈上分配, 暗含 "有界变量" 而不是 DST 之意, 此处故译为 "有界变量". 对于 DST, 只能使用智能指针指向于堆内存分配存放的数据, 栈上分配的空间是存放智能指针本体的, 而指针本体是有确切大小的) BK EX REF 在同步代码中存在于栈上. 在 async {} 中, 它们成为异步状态机的一部分, 可能存在于堆上.
3 Technically mutable and immutable are misnomer. Immutable binding or shared reference may still contain Cell STD, giving interior mutability.
从技术上讲, 可变不可变 是用词不当的. 不可变绑定或共享引用内部可能仍然包含 Cell STD, 从而提供 内部可变性.

 

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.
创建结构体 struct S {} 或使用已导入的枚举 enum E::S {}, 将字段 x 设置为 y.
S { x }Same, but use local variable x for field x.
一样, 但是使用本地变量 x 作为字段 x (译者注: 即同名的不用写成 x: x)
S { ..s }Fill remaining fields from s, esp. useful with Default::default(). STD
使用 s 里面的相应字段补全 (S 尚未设置的字段). 实用做法是和 Default::default(). STD搭配使用.
S { 0: x }Like S ​(x) below, but set field .0 with struct syntax.
和下面的 S ​(x) 类似, 但是使用结构体的语法, ("元组"结构体内部字段按数字顺序命名), 字段名称为 .0
S​ (x)Create struct S ​(T) or use'ed enum E::S​ () with field .0 set to x.
创建结构体 struct S ​(T) 或使用已导入的枚举 enum E::S​ (), 将字段 .0 设置为 x.
SIf S is unit struct S; or use'ed enum E::S create value of S.
S 可以是创建单元结构体 struct S; 的实例, 或者已导入的枚举 enum E::S
E::C { x: y }Create enum variant C. Other methods above also work.
创建枚举变体 C. 其他的和上面类似.
()Empty tuple, both literal and type, aka unit. STD
空元组, 既是字面量 (literal), 也是类型, 又称 unit. STD
(x)Parenthesized expression.
括号表达式.
(x,)Single-element tuple expression. EX STD REF
表达式之单元素元组. EX STD REF
(S,)Single-element tuple type.
类型之单元素元组.
[S]Array type of unspec. length, i.e., slice. EX STD REF Can't live on stack. *
未指定长度的数组类型, 即切片 (slice). EX STD REF 不能存在于栈上. *
[S; n]Array type EX STD REF of fixed length n holding elements of type S.
类型之数组 EX STD REF, 长度固定为 n, 即含 n 个类型为 S 的元素.
[x; n]Array instance REF (expression) with n copies of x.
数组实例 REF (表达式), 含 nx 的拷贝.
[x, y]Array instance with given elements x and y.
数组实例, 内含给定元素 x, y.
x[0]Collection indexing, here w. usize. Impl. via Index, IndexMut.
数组索引, 这里是一个数 (usize). 需要实现 Index, IndexMut 这两个 trait.
     x[..]Same, via range (here full range), also x[a..b], x[a..=b], … c. below.
类似, 不过是一个范围 (这里是 全部), 也有 x[a..b], x[a..=b], … 见下.
a..bRight-exclusive range STD REF creation, e.g., 1..3 means 1, 2.
a 到 b (不含 b) STD REF, 如, 1..31, 2.
(译者注: 这种有上下限的叫 Range, 范围)
..bRight-exclusive range to STD without starting point.
首到 b (不含 b) STD
(译者注: 这种只有上限的被称作 RangeTo, 范围 (从首部)...)
..=bInclusive range to STD without starting point.
首到 b (含 b) STD
a..=bInclusive range, STD 1..=3 means 1, 2, 3.
a 到 b (含 b) STD, 如, 1..=31, 2, 3.
a..Range from STD without ending point.
a 到末 STD.
(译者注: 这种只有下限的叫 RangeFrom, 范围从 ... (至末尾))
..Full range, STD usually means the whole collection.
全部, STD 常代指 整个集合.
(译者注: 这种叫 RangeFull, 范围(从首部至末尾))
s.xNamed field access, REF might try to Deref if x not part of type S.
访问命名字段, REF 可能尝试 解引用 (Deref), 如果 x 不是 S 的一部分的话 (译者注: 即类似 wrapper struct 的情况).
s.0Numbered field access, used for tuple types S ​(T).
访问编号字段, 用于元组类型 S ​(T)

* For now,RFC pending completion of tracking issue. 截至现在, RFC 依然有待 tracking issue 解决.

References & Pointersurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

引用和指针

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

访问非自已拥有的内存, 另请参阅章节 泛型与约束.

Example 示例Explanation 解释
&SShared reference BK STD NOM REF (type; space for holding any &s).
共享引用 BK STD NOM REF (类型; 用于存储 任意 引用 (&s) 的空间).
     &[S]Special slice reference that contains (addr, count).
特殊的对切片的引用类型, 内含内存地址及长度信息.
     &strSpecial string slice reference that contains (addr, byte_len).
特殊的对字符串切片的引用类型, 内含内存地址及字节长度信息.
     &mut SExclusive reference to allow mutability (also &mut [S], &mut dyn S, …).
独占引用 (类型. 又如 &mut [S], &mut dyn S, …).
(译者注: 字面意思是可变引用, 区分于共享引用, 作用域内只能有一个可变引用)
     &dyn TSpecial trait object BK REF ref. as (addr, vtable); T must be object safe. REF
特殊的对特质对象的引用 BK REF (类型), 内含内存地址及虚函数表 (vtable) 信息; T 必须是对象安全REF. (译者注: 现在改成 dyn-compatibility 了, 意义更明确, 可以 dyn, 更本质地: 编译器是否可以为该 trait 构造 vtable. 具体要求详见 REF)
&sShared borrow BK EX STD (e.g., addr., len, vtable, … of this s, like 0x1234).
共享借用 BK EX STD (一个包含了 对象 s 的内存地址、长度、虚函数表等元数据信息的指针, 形如 0x1234).
     &mut sExclusive borrow that allows mutability. EX
独占借用, 可变. EX
*const SImmutable raw pointer type BK STD REF w/o memory safety.
不可变 原始指针类型 BK STD REF, 没有任何内存安全保证.
     *mut SMutable raw pointer type w/o memory safety.
可变原始指针类型, 没有任何内存安全保证.
     &raw const sCreate raw pointer w/o going through ref.; c. ptr:addr_of!() STD 🧠
创建原始指针, 无需如 ptr:addr_of!() STD 🧠 这样的宏.
     &raw mut sSame, but mutable. 🚧 Needed for unaligned, packed fields. 🧠
创建可变原始指针 🚧, 在访问未对齐的 packed 的字段时使用. (译者注: 常见于涉及 FFI 的情况, 对接 C 侧代码)
ref sBind by reference, EX makes binding reference type. 🗑️
引用绑定, EX 🗑️
     let ref r = s;Equivalent to let r = &s.
等价于 let r = &s.
     let S { ref mut x } = s;Mut. ref binding (let x = &mut s.x), shorthand destructuring version.
可变引用绑定 (let x = &mut s.x), 简写解构 版本.
*rDereference BK STD NOM a reference r to access what it points to.
解引用 BK STD NOM r, 读取(指针)指向的数据.
     *r = s;If r is a mutable reference, move or copy s to target memory.
s 的拷贝 (当 SCopy 的时候) 或将 s 移动到 r 指向的内存区域(当然前提是 r 是可变引用).
     s = *r;Make s a copy of whatever r references, if that is Copy.
复制 r 引用的内容, 绑定为 s.
     s = *r;Won't work 🛑 if *r is not Copy, as that would move and leave empty.
如果 *rCopy, 这是不得行的 🛑, 因为这个操作会移动掉 r 指向的内容.
     s = *my_box;Special case 🔗 for BoxSTD that can move out b'ed content not Copy.
一个特例 🔗. 对于智能指针 BoxSTD, 解引用之会拿到其内部值 (译者注: 即 移动语义, 类似于对结构体的部分字段的移动).
'aA lifetime parameter, BK EX NOM REF duration of a flow in static analysis.
一个生命周期参数 BK EX NOM REF, 静态分析中代指一个流 (flow, 控制流? 数据流?) 的持续时间.
     &'a SOnly accepts address of some s; address existing 'a or longer.
类似 &S, 但要求其生命周期至少'a.
     &'a mut SSame, but allow address content to be changed.
类似 &mut S, 但要求其生命周期至少'a.
     struct S<'a> {}Signals this S will contain address with lt. 'a. Creator of S decides 'a.
结构体字段涉及引用时, 需要标注生命周期标记, 实例化时决定实际生命周期. (译者注: 特别地, 还有使用 PhantomData<&'a T> "取悦" 编译器的情况, 就算没有实际字段用到, 但是如实现一些 trait, 涉及到为一些引用类型实现, 而需要处理生命周期的时候)
     trait T<'a> {}Signals any S, which impl T for S, might contain address.
(译者注: 类似上面的情况, 不赘述)
     fn f<'a>(t: &'a T)Signals this function handles some address. Caller decides 'a.
(译者注: 类似上面的情况, 需要限定入参的生命周期, 如需要返回带引用的类型时. 当然, 大多数情况下编译器自动推断足矣)
'staticSpecial lifetime lasting the entire program execution.
一个特殊的生命周期, 意味着引用将与程序本体同寿.
译者补充: 也是相对的. 你硬编码的字面量 literal 是 'static 的, 常见于常量定义 (const I_AM_CONST_VAR: &'static str = "Hello world!";); 但是你肯定还见过 Cow<'static, str>, 即 Cow::Owned(***), *** 是一个 String, 不 Drop 掉它它都是与程序本体同寿的.

Functions & Behaviorurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

方法和行为

Define units of code and their abstractions.

代码单元及其抽象.

Example 实例Explanation 解释
trait T {}Define a trait; BK EX REF common behavior types can adhere to.
定义一个特质 (trait)BK EX REF: 用于定义可被各种类型实现的一类行为 (类似接口 interface).
(译者注: trait 作为 Rust 的专有概念, 译者认为应不译, 后文多数情况将保留不译, 此处首次出现, 给出其中文释义)
trait T : R {}T is subtrait of supertrait BK EX REF R. Any S must impl R before it can impl T.
T父 trait BK EX REF R 的子 trait. 即: 欲实现 T, 必须先实现 R.
impl S {}Implementation REF of functionality for a type S, e.g., methods.
类型 S实现 REF, 如各类方法.
impl T for S {}Implement trait T for type S; specifies how exactly S acts like T.
为类型 S 实现特质 T (内的各种方法).
impl !T for S {}Disable an automatically derived auto trait. NOM REF 🚧 🧠
阻止实现 auto trait T NOM REF 🚧 🧠 (译者注: auto trait, 编译器自动为类型实现的特质, 你可以通过这种方法手动阻止之, 常见于 async Rust 高阶实现中将一些 marker struct 指定为 Sync 的)
fn f() {}Definition of a function; BK EX REF or associated function if inside impl.
定义一个函数; BK EX REF 可以是一个 impl 内的关联函数 (关联方法?).
     fn f() -> S {}Same, returning a value of type S.
函数, 返回类型是 S.
     fn f(&self) {}Define a method, BK EX REF e.g., within an impl S {}.
方法 (译者注: 与特定数据类型关联的函数), BK EX REF, 常见于一个 impl 内的(关联)函数.
struct S ​(T);More arcanely, also defines fn S(x: T) -> S constructor fn. RFC 🧠
怪异地, 这 定义了一个 fn S(x: T) -> S构造方法.
const fn f() {}Constant fn usable at compile time, e.g., const X: u32 = f(Y). REF '18
const 方法 (编译时计算), 如 const X: u32 = f(Y). REF '18
     const { x }Used within a function, ensures { x } evaluated during compilation. REF
const 块, 告诉编译器在编译时计算 REF.
async fn f() {}Async REF '18 function transform, makes f return an impl Future. STD
异步 REF '18 函数, f 返回一个实现 Future. STD 的 opaque type.
译者补充: 这块需要给初学者指出, async 关键字算是一个语法糖, async fn f() -> R {} 脱糖后就是类似 fn f() -> impl Future<Output = R> {} 的样子. 当然, 这儿返回的是一个匿名结构体, 一个 "不透明" (opaque) 的类型, impl Future<Output = R> 是 RPIT (Return Position impl Trait, 返回值位置的 impl Trait) 的写法.
拓展到 trait 中的 async 关键字支持, 就是所谓的 AFIT (Async Function in Trait) 脱糖为 RPITIT (Return Position impl Trait in Trait) 了.
令人振奋的是, trait 内的 async 关键字支持 (AFIT) 于 Rust 1.75 稳定, RPITIT 也是. 在此之前只能用 async_trait 这种三方库处理在 trait 里面使用异步方法的情况, 返回类型相当难看, 简直是 Debug 火葬场.
     async fn f() -> S {}Same, but make f return an impl Future<Output=S>.
(译者注: 见上, 不赘述)
     async { x }Used within a function, make { x } an impl Future<Output=X>. REF
async 块, 生成一个 impl Future<Output = X> 的匿名结构体 REF.
译者注: 值得指出的是, 不同的 async 块生成的匿名结构体不是同一种类型, 所以你会见到 Box<dyn Future<Output = X>> 这种东西. 你说你只见过 Pin<Box<dyn Future<Output = X> + Send + Sync + 'static>>? 啊, 后面再详细解释.
     async move { x }Moves captured variables into future, c. move closure. REF
async 块, 但是将使用到的变量 "移动" 进去 REF .
译者注: 这里的 "'移动' 进去" 直接点就是将使用到的变量移动到块内部, 后面就不能用了.
译者的理解 💬: 本质是所谓异步状态机需要保存状态, 需要 "拥有" 使用到的数据. 对于借用, 借用的生命周期需要比这个异步匿名结构体长, 在异步执行的全程都必须 "活着". 但是一般来说异步任务是不知道啥时候执行完毕的, 所以一般只能是 'static 的借用才能保证这点. 而其他引用的数据就必须行 "移动" 语义了 (对于实现 Copy 的就是先拷贝一份再移动, 前面提到过).
fn() -> SFunction references, 1 BK STD REF memory holding address of a callable.
函数引用, 1 BK STD REF 指向内存中保存的可调用地址
Fn() -> SCallable trait BK STD (also FnMut, FnOnce), impl. by closures, fn's …
函数 trait BK STD (又如 FnMut, FnOnce), 闭包、函数等实现了此 trait.
AsyncFn() -> SCallable async trait STD (also AsyncFnMut, AsyncFnOnce), impl. by async c.
类似 Fn() -> S, 但异步版本 STD (又如 AsyncFnMut, AsyncFnOnce).
|| {} A closure BK EX REF that borrows its captures, REF (e.g., a local variable).
闭包 BK EX REF, 捕获(使用到的)变量的引用(如本地变量)
     |x| {}Closure accepting one argument named x, body is block expression.
接受一个参数 x 的闭包.
     |x| x + xSame, without block expression; may only consist of single expression.
同上, 但是单个表达式可以省略 {}.
     move |x| x + y Move closure REF taking ownership; i.e., y transferred into closure.
闭包, 但是取得(使用到的)变量的所有权(译者注: "移动" 语义, 不再赘述).
     async |x| x + xAsync closure. REF Converts its result into an impl Future<Output=X>.
异步闭包 REF, 返回类型是一个匿名结构体, 这个结构体 impl Future<Output=X>(类似 async 块).
     async move |x| x + yAsync move closure. Combination of the above.
(译者注: 不赘述)
     return || true Closures sometimes look like logical ORs (here: return a closure).
返回一个闭包, 长得像逻辑或.
unsafeIf you enjoy debugging segfaults; unsafe code. BK EX NOM REF
如果你喜欢调试段错误 (segfault); 不安全的代码. BK EX NOM REF
     unsafe fn f() {}Means "calling can cause UB, YOU must check requirements".
意味着 "调用可能导致 UB, 必须检查调用的前提要求".
     unsafe trait T {}Means "careless impl. of T can cause UB; implementor must check".意味着 "不正确的实现可能导致 UB, 你必须检查你的实现"
     unsafe { f(); }Guarantees to compiler "I have checked requirements, trust me".
告诉编译器对于这段不安全的操作: "检查过了, 相信我!"
     unsafe impl T for S {}Guarantees S is well-behaved w.r.t T; people may use T on S safely.
(类似地, 为 S 实现 unsafe trait T, T 的安全前提由实现本身保证, 调用者可以安全地调用)
     unsafe extern "abi" {}Starting with Rust 2024 extern "abi" {} blocks must be unsafe.
从 Rust 2024 始, extern "abi" {} 块都需要被标注为 unsafe (译者注: 这是语言层面的 breaking change, 所以放在了 Rust 大版本变化里).
          pub safe fn f();Inside an unsafe extern "abi" {}, mark f is actually safe to call. RFC
unsafe extern "abi" {} 里面确信安全的函数 f, 可以如此标注 RFC.

1 Most documentation calls them function pointers, but function references might be more appropriate 🔗 as they can't be null and must point to valid target. 多数文档称之为函数指针, 但函数引用的说法可能更为恰当 🔗, 因为其必须非空, 指向一个有效的目标.

Control Flowurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

控制流

Control execution within a function.

函数内部对执行过程的控制.

Example 实例Explanation 解释
while x {}Loop, REF run while expression x is true.
循环, REF, 循环条件是 x 为 true.
loop {}Loop indefinitely REF until break. Can yield value with break x.
死循环 REF, 直到循环体内部抛出 break x 退出. (译者注: 默认返回类型是 !, 也就是永不返回)
for x in collection {}Syntactic sugar to loop over iterators. BK STD REF
遍历**迭代器 (iterator)**的语法糖. BK STD REF
     ↪  collection.into_iter() Effectively converts any IntoIterator STD type into proper iterator first.
先将类型转换为合适的迭代器.
     ↪  iterator.next() On proper Iterator STD then x = next() until exhausted (first None).
对于迭代器, 获得一次迭代的结果, 直到迭代完毕(首次返回 None).
if x {} else {}Conditional branch REF if expression is true.
条件分支 REF.
'label: {}Block label, RFC can be used with break to exit out of this block. 1.65+
块标志, RFC, 可 break 'label 而离开此块. 1.65+
'label: loop {}Similar loop label, EX REF useful for flow control in nested loops.
类似, EX REF, 常见于复杂的多层循环的控制流控制.
breakBreak expression REF to exit a labelled block or loop.
Break 表达式, REF, 退出特定块或循环体.
     break 'label xBreak out of block or loop named 'label and make x its value.
退出块或循环体 'label, 返回值 x.
     break 'labelSame, but don't produce any value.
类似, 但没有返回值.
     break xMake x value of the innermost loop (only in actual loop).
跳出最接近的外层循环, 返回值 x.
continue Continue expression REF to the next loop iteration of this loop.
Continue 表达式 REF, 跳到下一轮循环.
continue 'labelSame but instead of this loop, enclosing loop marked with 'label.
类似地, 跳到下一轮循环 'label.
x?If x is Err or None, return and propagate. BK EX STD REF
如果 xErrNone, 立即短路返回(并向上传播错误 / None). BK EX STD REF
x.awaitSyntactic sugar to get future, poll, yield. REF '18 Only inside async.
(异步操作)语法糖.
译者注: 其作用为在此处 "等待" 当前 Future 执行结束返回 Poll::Ready($ret), $ret 即这个 Future 的返回值, 然后再继续执行下去. 当然, "等待" 一词其实不太恰当, 因为在轮询 (poll) 时遇到 Poll::Pending (异步操作尚未完成) 会让出 (yield) 资源, 让调度器调度别的异步任务. 具体细节见官方文档 REF.
     ↪  x.into_future() Effectively converts any IntoFuture STD type into proper future first.
将实现了 IntoFuture STD 这个 trait 的类型转换为对应的 Future.
     ↪  future.poll() On proper Future STD then poll() and yield flow if Poll::Pending. STD
对于特定 Future STD, poll() 之, 返回 Poll::PendingSTD 可让出 (yield).
return xEarly return REF from fn. More idiomatic is to end with expression.
在函数中提前返回 REF. 更地道的做法是以表达式结尾.
     { return }Inside normal {}-blocks return exits surrounding function.
在一般的块内 return 会直接从其所属函数返回.
     || { return }Within closures return exits that c. only, i.e., closure is s. fn.
对于闭包, 闭包块内的 return 作用域是当前闭包.
     async { return }Inside async a return only REF 🛑 exits that {}, i.e., async {} is s. fn.
特别地, async 块中的 return 作用域是当前块 REF 🛑, 毕竟 async 块通俗意义上是产生一个匿名结构体.
f()Invoke callable f (e.g., a function, closure, function pointer, Fn, …).
调用 f, f 可以是一个函数, 闭包, 函数指针, Fn, 等等.
x.f()Call member fn, requires f takes self, &self, … as first argument.
调用成员函数(方法), 需要 f 的首个参数是 self, &self, &hellip
     X::f(x)Same as x.f(). Unless impl Copy for X {}, f can only be called once.
类似, f 首个参数是 self, Rc<Self> 等, "移动" 语义.
     X::f(&x)Same as x.f().
类似, f 首个参数是 &self 等.
     X::f(&mut x)Same as x.f().
类似, f 首个参数是 &mut self.
     S::f(&x)Same as x.f() if X derefs to S, i.e., x.f() finds methods of S.
类似, 需要 X解引用S. (译者注: 对于 . 调用, 会自动解引用, 一般不需要这么写.)
     T::f(&x)Same as x.f() if X impl T, i.e., x.f() finds methods of T if in scope.
类似, 调用 trait T 的方法, (常见于多个 trait 用了同一个方法名字, 需要绝对形式限定 trait).
X::f()Call associated function, e.g., X::new().
调用关联函数 (译者注: 没参数那种), 如 X::new().
     <X as T>::f()Call trait method T::f() implemented for X.
调用 trait 方法 T::f(), 当然需要 X 实现了 T.

Organizing Codeurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

代码布局

Segment projects into smaller units and minimize dependencies.

把项目拆分为更小的单位, 最少化依赖.

Example 实例Explanation 解释
mod m {}Define a module, BK EX REF get definition from inside {}.
定义一个模块 (module), BK EX REF 内容在块内.
mod m;Define a module, get definition from m.rs or m/mod.rs.
类似, 但内容见同目录下同名文件 m.rs, 或同名子文件夹下 m/mod.rs (译者注, 现在不推荐后面的写法了).
a::bNamespace path EX REF to element b within a (mod, enum, …).
命名空间路径 (path) EX REF, 此处为 a (可以是 mod, enum, …) 中的元素 b.
     ::bSearch b in crate root '15 REF or ext. prelude; '18 REF global path. REF 🗑️
crate 根'15 REF外部预导入'18 REF中搜索b; 全局路径REF 🗑️.
     crate::bSearch b in crate root. '18
在 crate 根寻找 b. '18
     self::bSearch b in current module.
在当前模块寻找 b.
     super::bSearch b in parent module.
在父模块寻找 b.
use a::b;Use EX REF b directly in this scope without requiring a anymore.
直接使用 EX REF b, 不需要导入 a.
use a::{b, c};Same, but bring b and c into scope.
一样, 但是导入了 bc.
use a::b as x;Bring b into scope but name x, like use std::error::Error as E.
一样, 但是给 b 重命名为 x (以解决名称冲突等问题), 如 use std::error::Error as E.
use a::b as _;Bring b anon. into scope, useful for traits with conflicting names.
一样, 但匿名, 常用于处理 trait 名称冲突的问题.
use a::*;Bring everything from a in, only recomm. if a is some prelude. STD 🔗
a 导入所有, 仅当 a 是专用于预导入的模块时推荐这么做. STD 🔗
pub use a::b;Bring a::b into scope and reexport from here.
导入 a::b, 同时重新在当前模块导出之.
pub T"Public if parent path is public" visibility BK REF for T.
可见性标识: "父模块公开可见的则公开可见"
     pub(crate) TVisible at most1 in current crate.
至多1在当前 crate 可见.
     pub(super) TVisible at most1 in parent.
至多1在上一级模块可见.
     pub(self) TVisible at most1 in current module (default, same as no pub).
至多1在当前模块可见 (默认, 等价于没有 pub 标识).
     pub(in a::b) TVisible at most1 in ancestor a::b.
至多1a::b 模块内可见.
extern crate a;Declare dependency on external crate; BK REF 🗑️ just use a::b in '18.
导入外部 crate BK REF 🗑️ 已弃用, 自 '18 改用use a::b 的写法.
extern "C" {}Declare external dependencies and ABI (e.g., "C") from FFI. BK EX NOM REF
声明 来自 FFI 的外部依赖项和 ABI (例如"C"). BK EX NOM REF
extern "C" fn f() {}Define function to be exported with ABI (e.g., "C") to FFI.
声明 按特定 ABI (例如"C") 导出函数.

1 Items in child modules always have access to any item, regardless if pub or not. 子模块能访问父模块所有内容, 无论是不是 pub 的.

Type Aliases and Castsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

类型别名和类型转换

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.
S 的类型别名 BK REF (译者注: 一般用于简化过长类型名称, 如果需要为之实现外部 trait 建议使用 wrapper struct)
SelfType alias for implementing type, REF e.g., fn new() -> Self.
实现类型 (译者注: impl SS) 的别名 REF, 用法如 fn new() -> Self.
selfMethod subject BK REF in fn f(self) {}, e.g., akin to fn f(self: Self) {}.
(当前的)实例对象 (译者注: 类似于 this?), BK REF 在关联函数内使用: fn f(self) {} (等价于 fn f(self: Self) {}).
     &selfSame, but refers to self as borrowed, would equal f(self: &Self)
类似, 但是传实例对象的引用, 等价于 fn f(self: &Self) {}
     &mut selfSame, but mutably borrowed, would equal f(self: &mut Self)
类似, 但是传实例对象的可变引用, 等价于 fn f(self: &mut Self) {}
     self: Box<Self>Arbitrary self type, add methods to smart ptrs (my_box.f_of_self()).
任意 self 类型, 给智能指针添加一些方法 (如 my_box.f_of_self())
译者注: 一般而言, 会自动解引用, 但有时候需要直接操作智能指针本身.
<S as T>Disambiguate BK REF type S as trait T, e.g., <S as T>::f().
(泛型)消歧 BK REF, 类型 S 作为特质 T (而调用 T 定义的方法), 如<S as T>::f().
a::b as cIn use of symbol, import S as R, e.g., use a::S as R.
(译者注: 引入重命名, 见上, 不赘述)
x as u32Primitive cast, EX REF may truncate and be a bit surprising. 1 NOM
基本类型转型(类型转换), EX REF 可能出现截断等小惊喜. 1 NOM

1 See Type Conversions below for all the ways to convert between types. 见下面的类型转换, 内含更详细的方法.

Macros & Attributesurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

宏和属性

Code generation constructs expanded before the actual compilation happens.

实际编译前的代码生成(宏展开).

Example 实例Explanation 解释
m!()Macro BK STD REF invocation, also m!{}, m![] (depending on macro).
(过程) BK STD REF 调用. 也可能写成 m!{}, m![] (由实际实现决定).
#[attr]Outer attribute, EX REF annotating the following item.
附加*属性, EX REF 作用于紧邻着的项(item).
#![attr]Inner attribute, annotating the upper, surrounding item.
全局*属性, 作用于其所在位置的整个作用域.

* 译者注: 此处 Outer / Inner 直译实属不妥, 意译.

 
Inside Macros (过程)宏内 1Explanation 解释
$x:tyMacro capture, the :ty fragment specifier 2 REF declares what $x may be.
宏捕获, :ty 是片段分类符 2 REF, 指出意图捕获的 x 应当是什么东西 (译者注: 这里 ty 指类型. 受限于篇幅, 此处的前置知识 token tree 的概念不展开).
$xMacro substitution, e.g., use the captured $x:ty from above.
宏代入, 即在此处 "代入" 前面通过 $x:ty 之类的片段分类符捕获的片段.
$(x),*Macro repetition REF zero or more times.
重复 REF *, 可以捕获/代入多个 "重复" 片段. "*" 意思是重复 0 次及以上, "," 是匹配/代入多个重复的 x 时它们之间的分隔符 (代入时生成的 "重复" 代码间的分隔符).
     $(x),+Same, but one or more times.
类似, 但 "+" 意思是重复 1 次及以上.
     $(x)?Same, but zero or one time (separator doesn't apply).
类似, 但 "?" 意思是重复 0 或 1 次(有 or 没有).
     $(x)<<+In fact separators other than , are also accepted. Here: <<.
类似, 但重复的各个片段之间的分隔符可以是别的, 这里是 <<.

1 Applies to 'macros by example'. REF 参见 'macros by example'. REF (译者注: 此处过于抽象, 建议结合实例).
2 See Tooling Directives below for all fragment specifiers. 参见下方特殊 标记 参考了解目前支持的片段分类符.

Pattern Matchingurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

模式匹配

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

matchlet 表达式或函数参数中的 构造 *.

* 译者注: 此处翻译有待商榷.

Example 实例Explanation 解释
match m {}Initiate pattern matching, BK EX REF then use match arms, c. next table.
初始化一个模式匹配, BK EX REF 随后操作匹配分支. 具体参见下一个表格.
let S(x) = get();Notably, let also destructures EX similar to the table below.
值得指出, 参见下一个表格, let 也类似的解构 EX 用法.
     let S { x } = s;Only x will be bound to value s.x.
S 里面只有 x 一个字段, 解构之并绑定到同名变量 x (等价于 let x = s.x).
     let (_, b, _) = abc;Only b will be bound to value abc.1.
解构(含三个元素的)元组 abc, 仅将第二个元素绑定到变量 b, 忽略其他元素.
     let (a, ..) = abc;Ignoring 'the rest' also works.
解构元组 abc, 仅将第一个元素绑定到变量 a, 忽略余下其他元素.
     let (.., a, b) = (1, 2);Specific bindings take precedence over 'the rest', here a is 1, b is 2.
类似地, 不过是忽略倒数两个元素前面的元素, (就两个元素也可以,) 这里 a1, b2.
     let s @ S { x } = get();Bind s to S while x is bnd. to s.x, pattern binding, BK EX REF c. below 🧠
模式绑定, 即保留原来的值为 s 再解构 BK EX REF. 当然, 行 "移动" 语义, 需要字段 x 的类型是 Copy 的.
     let w @ t @ f = get();Stores 3 copies of get() result in each w, t, f. 🧠
     let (|x| x) = get();Pathological or-pattern, not closure.🛑 Same as let x = get(); 🧠
病态的 or 模式, 不是闭包.🛑 这里等价于 let x = get(); 🧠
译者注: 实在没看懂为什么, 反正别这么写就行...
let Ok(x) = f();Won't work 🛑 if p. can be refuted, REF use let else or if let instead.
错误的写法, 因为 Result 可能为 Err, 这种写法没有涵盖到所有可能分支. REF 请使用 let elseif let 的写法.
let Ok(x) = f();But can work if alternatives uninhabited, e.g., f returns Result<T, !> 1.82+
最近的版本这种写法也可能被接受: 当类型**不可实例化 (uninhabited)**时, 如 f 返回 Result<T, !> 1.82+, ! 就是个特殊的不可实例化的类型, 代表永不返回.
let Ok(x) = f() else {};Try to assign RFC if not else {} w. must break, return, panic!, … 1.65+ 🔥
当不匹配时执行 else 块的代码 (当然, else 分支只能是做 break, return, panic! 等跳出匹配). RFC 1.65+ 🔥
if let Ok(x) = f() {}Branch if pattern can be assigned (e.g., enum variant), syntactic sugar. *
if-let 语法糖*, 执行模式匹配, 若成功则执行代码块.
while let Ok(x) = f() {}Equiv.; here keep calling f(), run {} as long as p. can be assigned.
类似地, 若模式匹配成功则继续执行 while 循环块.
fn f(S { x }: S)Function param. also work like let, here x bound to s.x of f(s). 🧠
函数参数中的模式匹配, 类似地, 接受一个类型为 S 的参数, 后执行模式匹配. 🧠

* Desugars to match get() { Some(x) => {}, _ => () }. 脱糖为 match get() { Some(x) => {}, _ => () }.

 

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

match 表达式中的模式匹配分支. 这些分支的左侧同样可以出现在 let 表达式中.

Within Match ArmExplanation
E::A => {}Match enum variant A, c. pattern matching. BK EX REF
E::B ( .. ) => {}Match enum tuple variant B, ignoring any index.
E::C { .. } => {}Match enum struct variant C, ignoring any field.
S { x: 0, y: 1 } => {}Match s. with specific values (only s with s.x of 0 and s.y of 1).
S { x: a, y: b } => {}Match s. 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} | E::D {x} => {}Same, but bind x if all variants have it.
     Some(A | B) => {}Same, can also match alternatives deeply nested.
     |x| x => {}Pathological or-pattern,🛑 leading | ignored, is just x | x, thus 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. REF RFC
     [1, .., 5] => {}Match array starting with 1, ending with 5.
     [1, x @ .., 5] => {}Same, but also bind x to slice representing middle (c. pattern binding).
     [a, x @ .., b] => {}Same, but match any first, last, bound as a, b respectively.
1 .. 3 => {}Range pattern, BK REF here matches 1 and 2; partially unstable. 🚧
     1 ..= 3 => {}Inclusive range pattern, matches 1, 2 and 3.
     1 .. => {}Open range pattern, matches 1 and any larger number.
x @ 1..=5 => {}Bind matched to x; pattern binding, BK EX REF here x would be 15.
     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 & Constraintsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Generics combine with type constructors, traits and functions to give your users more flexibility.

Example 实例Explanation 解释
struct S<T>A generic BK EX type with a type parameter (T is placeholder here).
S<T> where T: RTrait bound, BK EX REF limits allowed T, guarantees T has trait R.
     where T: R, P: SIndependent trait bounds, here one for T and one for (not shown) P.
     where T: R, SCompile error, 🛑 you probably want compound bound R + S below.
     where T: R + SCompound trait bound, BK EX T must fulfill R and S.
     where T: R + 'aSame, but w. lifetime. T must fulfill R, if T has lt., must outlive 'a.
     where T: ?SizedOpt out of a pre-defined trait bound, here Sized. ?
     where T: 'aType lifetime bound; EX if T has references, they must outlive 'a.
     where T: 'staticSame; does not mean value t will 🛑 live 'static, only that it could.
     where 'b: 'aLifetime 'b must live at least as long as (i.e., outlive) 'a bound.
     where u8: R<T>Can also make conditional statements involving other types. 🧠
S<T: R>Short hand bound, almost same as above, shorter to write.
S<const N: usize>Generic const bound; REF user of type S can provide constant value N.
     S<10>Where used, const bounds can be provided as primitive values.
     S<{5+5}>Expressions must be put in curly brackets.
S<T = R>Default parameters; BK makes S a bit easier to use, but keeps flexible.
     S<const N: u8 = 0>Default parameter for constants; e.g., in f(x: S) {} param N is 0.
     S<T = u8>Default parameter for types, e.g., in f(x: S) {} param T is u8.
S<'_>Inferred anonymous lt.; asks compiler to 'figure it out' if obvious.
S<_>Inferred anonymous type, e.g., as let x: Vec<_> = iter.collect()
S::<T>Turbofish STD call site type disambiguation, e.g., f::<u32>().
     E::<T>::AGeneric enums can receive their type parameters on their type E
     E::A::<T>… or at the variant (A here); allows Ok::<R, E>(r) and similar.
trait T<X> {}A trait generic over X. Can have multiple impl T for S (one per X).
trait T { type X; }Defines associated type BK REF RFC X. Only one impl T for S possible.
trait T { type X<G>; }Defines generic associated type (GAT), RFC X can be generic Vec<>.
trait T { type X<'a>; }Defines a GAT generic over a lifetime.
     type X = R;Set associated type within impl T for S { type X = R; }.
     type X<G> = R<G>;Same for GAT, e.g., impl T for S { type X<G> = Vec<G>; }.
impl<T> S<T> {}Impl. fn's for any T in S<T> generically, REF here T ty. parameter.
impl S<T> {}Impl. fn's for exactly S<T> inherently, REF here T specific type, e.g., u8.
fn f() -> impl TExistential types, BK returns an unknown-to-caller S that impl T.
     -> impl T + 'aSignals the hidden type lives at least as long as 'a. RFC
     -> impl T + use<'a>Signals instead the hidden type captured lifetime 'a, use bound. 🔗 ?
     -> impl T + use<'a, R>Also signals the hidden type may have captured lifetimes from R.
fn f(x: &impl T)Trait bound via "impl traits", BK similar to fn f<S: T>(x: &S) below.
fn f(x: &dyn T)Invoke f via dynamic dispatch, BK REF f will not be instantiated for x.
fn f<X: T>(x: X)Fn. generic over X, f will be instantiated ('monomorphized') per X.
fn f() where Self: R;In trait T {}, make f accessible only on types known to also impl R.
     fn f() where Self: Sized;Using Sized can opt f out of trait object vtable, enabling dyn T.
     fn f() where Self: R {}Other R useful w. dflt. fn. (non dflt. would need be impl'ed anyway).

Higher-Ranked Items 🧠url 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Actual types and traits, abstract over something, usually lifetimes.

Example 实例Explanation 解释
for<'a>Marker for higher-ranked bounds. NOM REF 🧠
     trait T: for<'a> R<'a> {}Any S that impl T would also have to fulfill R for any lifetime.
fn(&'a u8)Function pointer type holding fn callable with specific lifetime 'a.
for<'a> fn(&'a u8)Higher-ranked type1 🔗 holding fn call. with any lt.; subtype of above.
     fn(&'_ u8)Same; automatically expanded to type for<'a> fn(&'a u8).
     fn(&u8)Same; automatically expanded to type for<'a> fn(&'a u8).
dyn for<'a> Fn(&'a u8)Higher-ranked (trait-object) type, works like fn above.
     dyn Fn(&'_ u8)Same; automatically expanded to type dyn for<'a> Fn(&'a u8).
     dyn Fn(&u8)Same; automatically expanded to type dyn for<'a> Fn(&'a u8).

1 Yes, the for<> is part of the type, which is why you write impl T for for<'a> fn(&'a u8) below.

 
Implementing TraitsExplanation
impl<'a> T for fn(&'a u8) {}For fn. pointer, where call accepts specific lt. 'a, impl trait T.
impl T for for<'a> fn(&'a u8) {}For fn. pointer, where call accepts any lt., impl trait T.
     impl T for fn(&u8) {}Same, short version.

Strings & Charsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Rust has several ways to create textual values.

Example 实例Explanation 解释
"..."String literal, REF, 1 a UTF-8 &'static str, STD supporting these escapes:
     "\n\r\t\0\\"Common escapes REF, e.g., "\n" becomes new line.
     "\x36"ASCII e. REF up to 7f, e.g., "\x36" would become 6.
     "\u{7fff}"Unicode e. REF up to 6 digits, e.g., "\u{7fff}" becomes 翿.
r"..."Raw string literal. REF, 1UTF-8, but won't interpret any escape above.
r#"..."#Raw string literal, UTF-8, but can also contain ". Number of # can vary.
c"..."C string literal, REF a NUL-terminated &'static CStr, STD for FFI. 1.77+
cr"...", cr#"..."#Raw C string literal, combination analog to above.
b"..."Byte string literal; REF, 1 constructs ASCII-only &'static [u8; N].
br"...", br#"..."#Raw byte string literal, combination analog to above.
b'x'ASCII byte literal, REF a single u8 byte.
'🦀'Character literal, REF fixed 4 byte unicode 'char'. STD

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.

Documentationurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Debuggers hate him. Avoid bugs with this one weird trick.

Example 实例Explanation 解释
///Outer line doc comment,1 BK EX REF use these on ty., traits, fn's, …
//!Inner line doc comment, mostly used at top of file.
//Line comment, use these to document code flow or internals.
/* … */Block comment. 2 🗑️
/** … */Outer block doc comment. 2 🗑️
/*! … */Inner block doc comment. 2 🗑️

1 Tooling Directives outline what you can do inside doc comments.
2 Generally discouraged due to bad UX. If possible use equivalent line comment instead with IDE support.

Miscellaneousurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

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

Example 实例Explanation 解释
!Always empty never type. BK EX STD REF
     fn f() -> ! {}Function that never ret.; compat. with any ty. e.g., let x: u8 = f();
     fn f() -> Result<(), !> {}Function that must return Result but signals it can never Err. 🚧
     fn f(x: !) {}Function that exists, but can never be called. Not very useful. 🧠 🚧
_Unnamed wildcard REF variable binding, e.g., |x, _| {}.
     let _ = x;Unnamed assign. is no-op, does not 🛑 move out x or preserve scope!
     _ = x;You can assign anything to _ without let, i.e., _ = ignore_rval(); 🔥
_xVariable binding that won't emit unused variable warnings.
1_234_567Numeric separator for visual clarity.
1_u8Type specifier for numeric literals EX REF (also i8, u16, …).
0xBEEF, 0o777, 0b1001Hexadecimal (0x), octal (0o) and binary (0b) integer literals.
r#fooA raw identifier BK EX for edition compatibility. 🧠
'r#aA raw lifetime label ? for edition compatibility. 🧠
x;Statement REF terminator, c. expressions EX REF

Common Operatorsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

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 Scenesurl

Arcane knowledge that may do terrible things to your mind, highly recommended.

The Abstract Machineurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Like C and C++, Rust is based on an abstract machine.

Rust CPU
🛑 Misleading.
Rust Abstract Machine CPU
Correct.
 

With rare exceptions you are never 'allowed to reason' about the actual CPU. You write code for an abstracted CPU. Rust then (sort of) understands what you want, and translates that into actual RISC-V / x86 / … machine code.

 

This 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,
  • is de-facto a contract between you and the compiler,
  • and exploits all of the above for optimizations.

On the left things people may incorrectly assume they should get away with if Rust targeted CPU directly. On the right things you'd interfere with if in reality if you violate the AM contract.

 
Without AMWith AM
0xffff_ffff would make a valid char. 🛑AM may exploit 'invalid' bit patterns to pack unrelated data.
0xff and 0xff are same pointer. 🛑AM pointers can have provenance STD for optimization.
Any r/w on pointer 0xff always fine. 🛑AM may issue cache-friendly ops since 'no read possible'.
Reading un-init just gives random value. 🛑AM 'knows' read impossible, may remove all related code.
Data race just gives random value. 🛑AM may split R/W, produce impossible value.
Null ref. is just 0x0 in some register. 🛑Holding 0x0 in reference summons Cthulhu.
 

This table is only to outline what the AM does. Unlike C or C++, Rust never lets you do the wrong thing unless you force it with unsafe.

Language Sugarurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

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

NameDescription
Coercions NOMWeakens types to match signature, e.g., &mut T to &T; c. type conv.
Deref NOM 🔗Derefs x: T until *x, **x, … compatible with some target S.
Prelude STDAutomatic import of basic items, e.g., Option, drop(), …
Reborrow 🔗Since x: &mut T can't be copied; moves new &mut *x instead.
Lifetime Elision BK NOM REFAllows you to write f(x: &T), instead of f<'a>(x: &'a T), for brevity.
Lifetime Extensions 🔗 REFIn let x = &tmp().f and similar hold on to temporary past line.
Method Resolution REFDerefs or borrow x until x.f() works.
Match Ergonomics RFCRepeatedly deref. scrutinee and adds ref and ref mut to bindings.
Rvalue Static Promotion RFC 🧠Makes refs. to constants 'static, e.g., &42, &None, &mut [].
Dual Definitions RFC 🧠Defining one (e.g., struct S(u8)) implicitly def. another (e.g., fn S).
Drop Hidden Flow REF 🧠At end of blocks { ... } or _ assignment, may call T::drop(). STD
Drop Not Callable STD 🧠Compiler forbids explicit T::drop() call, must use mem::drop(). STD
Auto Traits REFAlways impl'ed for your types, closures, futures if possible.
 

Opinion 💬 — These features make your life easier using Rust, but stand in the way of learning it. If you want to develop a genuine understanding, spend some extra time exploring them.

Memory & Lifetimesurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

An illustrated guide to moves, references and lifetimes.

Application Memory S(1) Application Memory
  • Application memory is just array of bytes on low level.
  • 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<T>),
    • static (most commonly used as resting place for str part of &str),
    • code (where bitcode of your functions reside).
  • Most tricky part is tied to how stack evolves, which is our focus.

1 For fixed-size values stack is trivially manageable: take a few bytes more while you need them, discarded once you leave. However, giving out pointers to these transient locations form the very essence of why lifetimes exist; and are the subject of the rest of this chapter.

Variables S(1) S(1) Variables
let t = S(1);
  • Reserves memory location with name t of type S and the value S(1) stored inside.
  • If declared with let that location lives on stack. 1
  • Note the linguistic ambiguity, in the term variable, it can mean the:
    1. name of the location in the source file ("rename that variable"),
    2. location in a compiled app, 0x7 ("tell me the address of that variable"),
    3. value contained within, S(1) ("increment that variable").
  • Specifically towards the compiler t can mean location of t, here 0x7, and value within t, here S(1).

1 Compare above, true for fully synchronous code, but async stack frame might placed it on heap via runtime.

Move Semantics S(1) 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 M { … } 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, …).
Scope & Drop S(1) C(2) S(2) S(3) 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().
Stack Frame S(1) 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) 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 { } 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.

Reference Types   S(1) 0x3 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   S(2) 0x3 S(1) 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) locations they point to.
  • The dereference *r means to use the location r points to (not the location of or value within r itself)
  • In the example, clone d is created from *r, and S(2) written to *r.
    • We assume S implements Clone, and r.clone() clones target-of-r, not r itself.
    • On assignment *r = old value in location also dropped (not shown above).
  S(2) 0x3 M { x } 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).
  C(2) 0x3 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.
C(2) 0x3 "Lifetime" of Things
  • Every entity in a program has some (temporal / spatial) extent where it is relevant, i.e., alive.
  • Loosely speaking, this alive time can be1
    1. the LOC (lines of code) where an item is available (e.g., a module name).
    2. the LOC between when a location is initialized with a value, and when the location is abandoned.
    3. the LOC between when a location is first used in a certain way, and when that usage stops.
    4. the LOC (or actual time) between when a value is created, and when that value is dropped.
  • Within the rest of this section, we will refer to the items above as the:
    1. scope of that item, irrelevant here.
    2. scope of that variable or location.
    3. lifetime2 of that usage.
    4. lifetime of that value, might be useful when discussing open file descriptors, but also irrelevant here.
  • Likewise, lifetime parameters in code, e.g., r: &'a S, are
    • concerned with LOC any location r points to needs to be accessible or locked;
    • unrelated to the 'existence time' (as LOC) of r itself (well, it needs to exist shorter, that's it).
  • &'static S means address must be valid during all lines of code.

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 …

  S(0) S(1) S(2) 0xa 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.
  S(0) S(3) S(2) 0x6 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 assigning &b (0x6) to r is valid, but &a (0x3) would not, as only &b lives equal or longer than &c.
  S(0)   S(2) 0x6 S(4) 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 address cannot be modified anymore via original binding b.
  • Once address taken via &b or &mut b stops being used (in terms of LOC) original binding b works again.
S(0) S(1) S(2) ? 0x6 0xa 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 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.
  S(1) S(1) S(2) y + _ 0x6 0xa 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);
  • Lifetime 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 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.
      S(1) 0x2 0x6 0x2 References to References
// Return short ('b) reference
fn f1sr<'b, 'a>(rb: &'b     &'a     S) -> &'b     S { *rb }
fn f2sr<'b, 'a>(rb: &'b     &'a mut S) -> &'b     S { *rb }
fn f3sr<'b, 'a>(rb: &'b mut &'a     S) -> &'b     S { *rb }
fn f4sr<'b, 'a>(rb: &'b mut &'a mut S) -> &'b     S { *rb }

// Return short ('b) mutable reference.
// f1sm<'b, 'a>(rb: &'b     &'a     S) -> &'b mut S { *rb } // M
// f2sm<'b, 'a>(rb: &'b     &'a mut S) -> &'b mut S { *rb } // M
// f3sm<'b, 'a>(rb: &'b mut &'a     S) -> &'b mut S { *rb } // M
fn f4sm<'b, 'a>(rb: &'b mut &'a mut S) -> &'b mut S { *rb }

// Return long ('a) reference.
fn f1lr<'b, 'a>(rb: &'b     &'a     S) -> &'a     S { *rb }
// f2lr<'b, 'a>(rb: &'b     &'a mut S) -> &'a     S { *rb } // L
fn f3lr<'b, 'a>(rb: &'b mut &'a     S) -> &'a     S { *rb }
// f4lr<'b, 'a>(rb: &'b mut &'a mut S) -> &'a     S { *rb } // L

// Return long ('a) mutable reference.
// f1lm<'b, 'a>(rb: &'b     &'a     S) -> &'a mut S { *rb } // M
// f2lm<'b, 'a>(rb: &'b     &'a mut S) -> &'a mut S { *rb } // M
// f3lm<'b, 'a>(rb: &'b mut &'a     S) -> &'a mut S { *rb } // M
// f4lm<'b, 'a>(rb: &'b mut &'a mut S) -> &'a mut S { *rb } // L

// Now assume we have a `ra` somewhere
let mut ra: &'a mut S =;

let rval = f1sr(&&*ra);       // OK
let rval = f2sr(&&mut *ra);
let rval = f3sr(&mut &*ra);
let rval = f4sr(&mut ra);

//  rval = f1sm(&&*ra);       // Would be bad, since rval would be mutable
//  rval = f2sm(&&mut *ra);   // reference obtained from broken mutability
//  rval = f3sm(&mut &*ra);   // chain.
let rval = f4sm(&mut ra);

let rval = f1lr(&&*ra);
//  rval = f2lr(&&mut *ra);   // If this worked we'd have `rval` and `ra` …
let rval = f3lr(&mut &*ra);
//  rval = f4lr(&mut ra);     // … now (mut) aliasing `S` in compute below.

//  rval = f1lm(&&*ra);       // Same as above, fails for mut-chain reasons.
//  rval = f2lm(&&mut *ra);   //                    "
//  rval = f3lm(&mut &*ra);   //                    "
//  rval = f4lm(&mut ra);     // Same as above, fails for aliasing reasons.

// Some fictitious place where we use `ra` and `rval`, both alive.
compute(ra, rval);

Here (M) means compilation fails because mutability error, (L) lifetime error. Also, dereference *rb not strictly necessary, just added for clarity.

  • f_sr cases always work, short reference (only living 'b) can always be produced.
  • f_sm cases usually fail simply because mutable chain to S needed to return &mut S.
  • f_lr cases can fail because returning &'a S from &'a mut S to caller means there would now exist two references (one mutable) to same S which is illegal.
  • f_lm cases always fail for combination of reasons above.

Note: This example is about the f functions, not compute. You can assume it to be defined as fn compute(x: &S, y: &S) {}. In that case the ra parameter would be automatically coerced from &mut S to &S, since you can't have a shared and a mutable reference to the same target.

S(1) Drop and _
{
    let f = |x, y| (S(x), S(y)); // Function returning two 'Droppables'.

    let (    x1, y) = f(1, 4);  // S(1) - Scope   S(4) - Scope
    let (    x2, _) = f(2, 5);  // S(2) - Scope   S(5) - Immediately
    let (ref x3, _) = f(3, 6);  // S(3) - Scope   S(6) - Scope

    println!("…");
}

Here Scope means contained value lives until end of scope, i.e., past the println!().

  • Functions or expressions producing movable values must be handled by callee.
  • Values stores in 'normal' bindings are kept until end of scope, then dropped.
  • Values stored in _ bindings are usually dropped right away.
  • However, sometimes references (e.g., ref x3) can keep value (e.g., the tuple (S(3), S(6))) around for longer, so S(6), being part of that tuple can only be dropped once reference to its S(3) sibling disappears).

↕️ Examples expand by clicking.

 

Memory Layouturl

Byte representations of common types.

Basic Typesurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Essential types built into the core of the language.

Boolean REF and Numeric Types REFurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

bool u8, i8 u16, i16 u32, i32 u64, i64 u128, i128 usize, isize Same as ptr on platform. f16 🚧 f32 f64 f128 🚧
 
TypeMax Value
u8255
u1665_535
u324_294_967_295
u6418_446_744_073_709_551_615
u128340_282_366_920_938_463_463_374_607_431_768_211_455
usizeDepending on platform pointer size, same as u16, u32, or u64.
TypeMax Value
i8127
i1632_767
i322_147_483_647
i649_223_372_036_854_775_807
i128170_141_183_460_469_231_731_687_303_715_884_105_727
isizeDepending on platform pointer size, same as i16, i32, or i64.
 
TypeMin 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
isizeDepending on platform pointer size, same as i16, i32, or i64.
TypeMax valueMin pos valueMax lossless integer1
f16 🚧65504.06.10 ⋅ 10 -52048
f323.40 ⋅ 10 383.40 ⋅ 10 -3816_777_216
f641.79 ⋅ 10 3082.23 ⋅ 10 -3089_007_199_254_740_992
f128 🚧1.19 ⋅ 10 49323.36 ⋅ 10 -49322.07 ⋅ 10 34

1 The maximum integer M so that all other integers 0 <= X <= M can be losslessly represented in that type. In other words, there might be larger integers that could still be represented losslessly (e.g., 65504 for f16), but up until that value a lossless representation is guaranteed.

 

Float values approximated for visual clarity. Negative limits are values multipled with -1.

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:

f32S (1)E (8)F (23)Value
Normalized number±1 to 254any±(1.F)2 * 2E-127
Denormalized number±0non-zero±(0.F)2 * 2-126
Zero±00±0
Infinity±2550±∞
NaN±255non-zeroNaN
 

Similarly, for f64 types this would look like:

f64S (1)E (11)F (52)Value
Normalized number±1 to 2046any±(1.F)2 * 2E-1023
Denormalized number±0non-zero±(0.F)2 * 2-1022
Zero±00±0
Infinity±20470±∞
NaN±2047non-zeroNaN
* Float types follow IEEE 754-2008 and depend on platform endianness.
Cast1GivesNote
3.9_f32 as u83Truncates, consider x.round() first.
314_f32 as u8255Takes closest available number.
f32::INFINITY as u8255Same, treats INFINITY as really large number.
f32::NAN as u80-
_314 as u858Truncates excess bits.
_257 as i81Truncates excess bits.
_200 as i8-56Truncates excess bits, MSB might then also signal negative.
Operation1GivesNote
200_u8 / 0_u8Compile error.-
200_u8 / _0 d, rPanic.Regular math may panic; here: division by zero.
200_u8 + 200_u8Compile error.-
200_u8 + _200 dPanic.Consider checked_, wrapping_, … instead. STD
200_u8 + _200 r144In release mode this will overflow.
-128_i8 * -1Compile error.Would overflow (128_i8 doesn't exist).
-128_i8 * _1neg dPanic.-
-128_i8 * _1neg r-128Overflows back to -128 in release mode.
1_u8 / 2_u80Other integer division truncates.
0.8_f32 + 0.1_f320.90000004-
1.0_f32 / 0.0_f32f32::INFINITY-
0.0_f32 / 0.0_f32f32::NAN-
x < f32::NANfalseNAN comparisons always return false.
x > f32::NANfalseNAN comparisons always return false.
f32::NAN == f32::NANfalseUse f32::is_nan() STD instead.

1 Expression _100 means anything that might contain the value 100, e.g., 100_i32, but is opaque to compiler.
d Debug build.
r Release build.

 

Textual Types REFurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

char Any Unicode scalar. str U T F - 8 … unspecified times Rarely seen alone, but as &str instead.
 
TypeDescription
charAlways 4 bytes and only holds a single Unicode scalar value 🔗.
strAn u8-array of unknown length guaranteed to hold UTF-8 encoded code points.
CharsDescription
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 = 0xffff_ffff;Also, chars are not allowed 🛑 to hold arbitrary bit 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.
 
StringsDescription
let s = "a";A str is usually never held directly, but as &str, like s here.
let s = "❤❤️";It can hold arbitrary text, has variable length per c., and is hard to index.

let s = "I ❤ Rust";
let t = "I ❤️ Rust";

VariantMemory Representation2
s.as_bytes()49 20 e2 9d a4 20 52 75 73 74 3
t.as_bytes()49 20 e2 9d a4 ef b8 8f 20 52 75 73 74 4
s.chars()149 00 00 00 20 00 00 00 64 27 00 00 20 00 00 00 52 00 00 00 75 00 00 00 73 00
t.chars()149 00 00 00 20 00 00 00 64 27 00 00 0f fe 01 00 20 00 00 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 Typesurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Basic types definable by users. Actual layout REF is subject to representation; REF padding can be present.

T T Sized type. T: ?Sized T Maybe sized. [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 type. (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() STD 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 squeeze tag
into 'unused' bits.
union {} A unsafe or B unsafe or C Can unsafely reinterpret
memory. Result might
be undefined.

References & Pointersurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

References give safe access to 3rd party memory, raw pointers unsafe access. The corresponding mut types have an identical data layout to their immutable counterparts.

&'a T ptr2/4/8 meta2/4/8 | T Must target some valid t of T,
and any such target must exist for
at least 'a.
*const T ptr2/4/8 meta2/4/8 No guarantees.

Pointer Metaurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

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 ptr2/4/8 | T No meta for
sized target.
(pointer is thin).
&'a T ptr2/4/8 len2/4/8 | T If T is a DST struct such as
S { x: [u8] } meta field len is
count of dyn. sized content.
&'a [T] ptr2/4/8 len2/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 ptr2/4/8 len2/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 ptr2/4/8 ptr2/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.

Closuresurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Ad-hoc functions with an automatically managed data block capturing REF, 1 environment where closure was defined. For example, if you had:

let y = ...;
let z = ...;

with_closure(move |x| x + y.f() + z); // y and z are moved into closure instance (of type C1)
with_closure(     |x| x + y.f() + z); // y and z are pointed at from closure instance (of type C2)

Then the generated, anonymous closures types C1 and C2 passed to with_closure() would look like:

move |x| x + y.f() + z Y Z Anonymous closure type C1 |x| x + y.f() + z ptr2/4/8 ptr2/4/8 Anonymous closure type C2 | Y | Z

Also produces anonymous fn such as fc1(C1, X) or fc2(&C2, X). Details depend on which FnOnce, FnMut, Fn ... is supported, based on properties of captured types.

1 A bit oversimplified a closure is a convenient-to-write 'mini function' that accepts parameters but also needs some local variables to do its job. It is therefore a type (containing the needed locals) and a function. 'Capturing the environment' is a fancy way of saying that and how the closure type holds on to these locals, either by moved value, or by pointer. See Closures in APIs for various implications.

Standard Library Typesurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Rust's standard library combines the above primitive types into useful types with special semantics, e.g.:

Option<T> STD Tag or Tag T Tag may be omitted for
certain T, e.g., NonNull.STD
Result<T, E> STD Tag E or Tag T Either some error E or value
of T.
ManuallyDrop<T> STD T Prevents T::drop() from
being called.
AtomicUsize STD usize2/4/8 Other atomic similarly. MaybeUninit<T> STD U̼̟̔͛n̥͕͐͞d̛̲͔̦̳̑̓̐e̱͎͒̌fị̱͕̈̉͋ne̻̅ḓ̓ unsafe or T Uninitialized memory or
some T. Only legal way
to work with uninit data.
PhantomData<T> STD Zero-sized helper to hold
otherwise unused lifetimes.
Pin<P> STD P | 📌 P::Deref Signals tgt. of P is pinned 'forever'
even past lt. of Pin. Value within
may not be moved out (but new
one moved in), unless Unpin.STD

🛑 All depictions are for illustrative purposes only. The fields should exist in latest stable, but Rust makes no guarantees about their layouts, and you must not attempt to unsafely access anything unless the docs allow it.

 

Cellsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

UnsafeCell<T> STD T Magic type allowing
aliased mutability.
Cell<T> STD T Allows T's
to move in
and out.
RefCell<T> STD borrowed T Also support dynamic
borrowing of T. Like Cell this
is Send, but not Sync.
OnceCell<T> STD
Tag or Tag T
Initialized at most once.
LazyCell<T, F> STD
Tag Uninit<F> or Tag Init<T> or Tag Poisoned
Initialized on first access.
 

Order-Preserving Collectionsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Box<T> STD ptr2/4/8 meta2/4/8 | T For some T stack proxy may carry
meta (e.g., Box<[T]>).
Vec<T> STD ptr2/4/8 len2/4/8 capacity2/4/8 |
T T … len
capacity
Regular growable array vector of single type.
LinkedList<T> STD🧠 head2/4/8 tail2/4/8 len2/4/8 | | next2/4/8 prev2/4/8 T Elements head and tail both null or point to nodes on
the heap. Each node can point to its prev and next node.
Eats your cache (just look at the thing!); don't use unless
you evidently must. 🛑
VecDeque<T> STD head2/4/8 len2/4/8 ptr2/4/8 capacity2/4/8 |
T … empty … TH
capacity
Index head selects in array-as-ringbuffer. This means content may be
non-contiguous and empty in the middle, as exemplified above.
 

Other Collectionsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

HashMap<K, V> STD bmask2/4/8 ctrl2/4/8 left2/4/8 len2/4/8 | K:V K:VK:VK:V Oversimplified! Stores keys and values on heap according to hash value, SwissTable
implementation via hashbrown. HashSet STD identical to HashMap,
just type V disappears. Heap view grossly oversimplified. 🛑
BinaryHeap<T> STD ptr2/4/8 capacity2/4/8 len2/4/8 |
T0 T1 T1 T2 T2 … len
capacity
Heap stored as array with 2N elements per layer. Each T
can have 2 children in layer below. Each T larger than its
children.

Owned Stringsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

String STD ptr2/4/8 capacity2/4/8 len2/4/8 |
U T F - 8 … len
capacity
Observe how String differs from &str and &[char].
CString STD ptr2/4/8 len2/4/8 |
A B C … len …
NUL-terminated but w/o NUL in middle.
OsString STD Platform Defined |
/
Encapsulates how operating system
represents strings (e.g., WTF-8 on
Windows).
PathBuf STD OsString |
/
Encapsulates how operating system
represents paths.
 

Shared Ownershipurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

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

Rc<T> STD ptr2/4/8 meta2/4/8
| strng2/4/8 weak2/4/8 T
Share ownership of T in same thread. Needs nested Cell
or RefCellto allow mutation. Is neither Send nor Sync.
Arc<T> STD ptr2/4/8 meta2/4/8
| strng2/4/8 weak2/4/8 T
Same, but allow sharing between threads IF contained
T itself is Send and Sync.

Mutex<T> STD / RwLock<T> STD inner poison2/4/8 T Inner fields depend on platform. Needs to be
held in Arc to be shared between decoupled
threads, or via scope() STD for scoped threads.
Cow<'a, T> STD Tag T::Owned or Tag ptr2/4/8
| T
Holds read-only reference to
some T, or owns its ToOwned STD
analog.

Standard Libraryurl

One-Linersurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Snippets that are common, but still easy to forget. See Rust Cookbook 🔗 for more.

IntentSnippet
Concatenate strings (any Display that is). STD 1 '21format!("{x}{y}")
Append string (any Display to any Write). '21 STDwrite!(x, "{y}")
Split by separator pattern. STD 🔗s.split(pattern)
     … with &strs.split("abc")
     … with chars.split('/')
     … with closures.split(char::is_numeric)
Split by whitespace. STDs.split_whitespace()
Split by newlines. STDs.lines()
Split by regular expression. 🔗 2 Regex::new(r"\s")?.split("one two three")

1 Allocates; if x or y are not going to be used afterwards consider using write! or std::ops::Add.
2 Requires regex crate.

IntentSnippet
Create a new file STDFile::create(PATH)?
     Same, via OpenOptionsOpenOptions::new().create(true).write(true).truncate(true).open(PATH)?
Read file as String STDread_to_string(path)?
IntentSnippet
Macro w. variable argumentsmacro_rules! var_args { ($($args:expr),*) => {{ }} }
     Using args, e.g., calling f multiple times.     $( f($args); )*
Starting TypeResource
Option<T> ->See the Type-Based Cheat Sheet
Result<T, R> ->See the Type-Based Cheat Sheet
Iterator<Item=T> ->See the Type-Based Cheat Sheet
&[T] ->See the Type-Based Cheat Sheet
Future<T> ->See the Futures Cheat Sheet
IntentSnippet
Cleaner closure captureswants_closure({ let c = outer.clone(); move || use_clone(c) })
Fix inference in 'try' closuresiter.try_for_each(|x| { Ok::<(), Error>(()) })?;
Iterate and edit &mut [T] if T Copy.Cell::from_mut(mut_slice).as_slice_of_cells()
Get subslice with length.&original_slice[offset..][..length]
Canary so trait T is object safe. REFconst _: Option<&dyn T> = None;
Semver trick to unify types. 🔗my_crate = "next.version" in Cargo.toml + re-export types.
Use macro inside own crate. 🔗macro_rules! internal_macro {} with pub(crate) use internal_macro;

Thread Safetyurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Assume you hold some variables in Thread 1, and want to either move them to Thread 2, or pass their references to Thread 3. Whether this is allowed is governed by SendSTD and SyncSTD respectively:

 

|
|
|
 Mutex<u32>
|
|
|
 Cell<u32>
|
|
|
 MutexGuard<u32>
|
|
|
 Rc<u32>
Thread 1

 Mutex<u32>  Cell<u32>  MutexGuard<u32>  Rc<u32> Thread 2

&Mutex<u32> &Cell<u32> &MutexGuard<u32> &Rc<u32> Thread 3
 
Example 实例Explanation 解释
Mutex<u32>Both Send and Sync. You can safely pass or lend it to another thread.
Cell<u32>Send, not Sync. Movable, but its reference would allow concurrent non-atomic writes.
MutexGuard<u32>Sync, but not Send. Lock tied to thread, but reference use could not allow data race.
Rc<u32>Neither since it is easily clonable heap-proxy with non-atomic counters.
 
TraitSend!Send
SyncMost typesArc<T>1,2, Mutex<T>2MutexGuard<T>1, RwLockReadGuard<T>1
!SyncCell<T>2, RefCell<T>2Rc<T>, &dyn Trait, *const T3

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.

 
When is ...... Send?
TAll contained fields are Send, or unsafe impl'ed.
    struct S { ... }All fields are Send, or unsafe impl'ed.
    struct S<T> { ... }All fields are Send and T is Send, or unsafe impl'ed.
    enum E { ... }All fields in all variants are Send, or unsafe impl'ed.
&TIf T is Sync.
|| {}Closures are Send if all captures are Send.
     |x| { }Send, regardless of x.
     |x| { Rc::new(x) }Send, since still nothing captured, despite Rc not being Send.
     |x| { x + y }Only Send if y is Send.
async { }Futures are Send if no !Send is held over .await points.
     async { Rc::new() }Future is Send, since the !Send type Rc is not held over .await.
     async { rc; x.await; rc; } 1Future is !Send, since Rc used across the .await point.
async || { } 🚧Async cl. Send if all cpts. Send, res. Future if also no !Send inside.
     async |x| { x + y } 🚧Async closure Send if y is Send. Future Send if x and y Send.

1 This is a bit of pseudo-code to get the point across, the idea is to have an Rc before an .await point and keep using it beyond that point.

Atomics & Cache 🧠url 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

CPU cache, memory writes, and how atomics affect it.

S O M E D R A M D A T A Main Memory S O M E (E) D A T A (S) CPU1 Cache S R A M (M) D A T A (S) CPU2 Cache

Modern CPUs don't accesses memory directly, only their cache. Each CPU has its own cache, 100x faster than RAM, but much smaller. It comes in cache lines,🔗 some sliced window of bytes, which track if it's an exclusive (E), shared (S) or modified (M) 🔗 view of the main memory. Caches talk to each other to ensure coherence,🔗 i.e., 'small-enough' data will be 'immediately' seen by all other CPUs, but that may stall the CPU.

S O M E D X T A (M) Cycle 1 O3 S O M 4 D X T A Cycle 2 23 STALLED 1 4 (M) D X T Y (M) Cycle 3 23

Left: Both compiler and CPUs are free to re-order 🔗 and split R/W memory access. Even if you explicitly said write(1); write(23); write(4), your compiler might think it's a good idea to write 23 first; in addition your CPU might insist on splitting the write, doing 3 before 2. Each of these steps could be observable (even the impossible O3) by CPU2 via an unsafe data race. Reordering is also fatal for locks.

Right: Semi-related, even when two CPUs do not attempt to access each other's data (e.g., update 2 independent variables), they might still experience a significant performance loss if the underlying memory is mapped by 2 cache lines (false sharing).🔗

1 2 3 4 S R A M D X T Y Main Memory RA 1 R A M Cycle 4 RA 1 2 M Cycle 5 23 1 2 3 4 (M) Cycle 6 23

Atomics address the above issues by doing two things, they

  • make sure a read / write / update is not partially observable by temporarily locking cache lines in other CPUs,
  • force both the compiler and the CPU to not re-order 'unrelated' access around it (i.e., act as a fence STD). Ensuring multiple CPUs agree on the relative order of these other ops is called consistency. 🔗 This also comes at a cost of missed performance optimizations.
 

Note — The above section is greatly simplified. While the issues of coherence and consistency are universal, CPU architectures differ a lot in how they implement caching and atomics, and in their performance impact.

 
     A. OrderingExplanation
Relaxed STDFull reordering. Unrelated R/W can be freely shuffled around the atomic.
Release STD, 1When writing, ensure other data loaded by 3rd party Acquire is seen after this write.
Acquire STD, 1When reading, ensures other data written before 3rd party Release is seen after this read.
SeqCst STDNo reordering around atomic. All unrelated reads and writes stay on proper side.

1 To be clear, when synchronizing memory access with 2+ CPUs, all must use Acquire or Release (or stronger). The writer must ensure that all other data it wishes to release to memory are put before the atomic signal, while the readers who wish to acquire this data must ensure that their other reads are only done after the atomic signal.

Iteratorsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Processing elements in a collection.

There are, broadly speaking, four styles of collection iteration:

StyleDescription
for x in c { ... }Imperative, useful w. side effects, interdepend., or need to break flow early.
c.iter().map().filter()Functional, often much cleaner when only results of interest.
c_iter.next()Low-level, via explicit Iterator::next() STD invocation. 🧠
c.get(n)Manual, bypassing official iteration machinery.
 

Opinion 💬 — Functional style is often easiest to follow, but don't hesitate to use for if your .iter() chain turns messy. When implementing containers iterator support would be ideal, but when in a hurry it can sometimes be more practical to just implement .len() and .get() and move on with your life.

Basics

Assume you have a collection c of type C you want to use:

  • c.into_iter()1 — Turns collection c into an Iterator STD i and consumes2 c. Std. way to get iterator.
  • 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.

1 Requires IntoIterator STD for C to be implemented. Type of item depends on what C was.

2 If it looks as if it doesn't consume c that's because type was Copy. For example, if you call (&c).into_iter() it will invoke .into_iter() on &c (which will consume a copy of the reference and turn it into an Iterator), but the original c remains untouched.

Essentials

Let's assume you have a struct Collection<T> {} you authored. You should also implement:

  • struct IntoIter<T> {} — Create a struct to hold your iteration status (e.g., an index) for value iteration.
  • impl Iterator for IntoIter<T> {} — Implement Iterator::next() so it can produce elements.
Collection<T> IntoIter<T> Iterator Item = T;
 

At this point you have something that can behave as an Iterator, STD but no way of actually obtaining it. See the next tab for how that usually works.

Native Loop Support

Many users would expect your collection to just work in for loops. You need to implement:

  • impl IntoIterator for Collection<T> {} — Now for x in c {} works.
  • impl IntoIterator for &Collection<T> {} — Now for x in &c {} works.
  • impl IntoIterator for &mut Collection<T> {} — Now for x in &mut c {} works.
Collection<T> IntoIterator Item = T; To = IntoIter<T> Iterate over T. &Collection<T> IntoIterator Item = &T; To = Iter<T> Iterate over &T. &mut Collectn<T> IntoIterator Item = &mut T; To = IterMut<T> Iterate over &mut T.
 

As you can see, the IntoIterator STD trait is what actually connects your collection with the IntoIter struct you created in the previous tab. The two siblings of IntoIter (Iter and IterMut) are discussed in the next tab.

Shared & Mutable Iterators

In addition, if you want your collection to be useful when borrowed you should implement:

  • struct Iter<T> {} — Create struct holding &Collection<T> state for shared iteration.
  • struct IterMut<T> {} — Similar, but holding &mut Collection<T> state for mutable iteration.
  • impl Iterator for Iter<T> {} — Implement shared iteration.
  • impl Iterator for IterMut<T> {} — Implement mutable iteration.

Also you might want to add convenience methods:

  • Collection::iter(&self) -> Iter,
  • Collection::iter_mut(&mut self) -> IterMut.
Iter<T> Iterator Item = &T; IterMut<T> Iterator Item = &mut T;
 

The code for borrowing interator support is basically just a repetition of the previous steps with a slightly different types, e.g., &T vs T.

Iterator Interoperability

To allow 3rd party iterators to 'collect into' your collection implement:

  • impl FromIterator for Collection<T> {} — Now some_iter.collect::<Collection<_>>() works.
  • impl Extend for Collection<T> {} — Now c.extend(other) works.

In addition, also consider adding the extra traits from std::iter STD to your previous structs:

Collection<T> FromIterator Extend IntoIter<T> DoubleEndedIt ExactSizeIt FusedIterator Iter<T> DoubleEndedIt ExactSizeIt FusedIterator IterMut<T> DoubleEndedIt ExactSizeIt FusedIterator
 

Writing collections can be work. The good news is, if you followed all these steps your collections will feel like first class citizens.

Number Conversionsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

As-correct-as-it-currently-gets number conversions.

↓ Have / Want →u8i128f32 / f64String
u8i128u8::try_from(x)? 1x as f32 3x.to_string()
f32 / f64x as u8 2x as f32x.to_string()
Stringx.parse::<u8>()?x.parse::<f32>()?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).

 

Also see Casting- and Arithmetic Pitfalls for more things that can go wrong working with numbers.

String Conversionsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

If you want a string of type …

If you have x of type …Use this …
Stringx
CStringx.into_string()?
OsStringx.to_str()?.to_string()
PathBufx.to_str()?.to_string()
Vec<u8> 1String::from_utf8(x)?
&strx.to_string() i
&CStrx.to_str()?.to_string()
&OsStrx.to_str()?.to_string()
&Pathx.to_str()?.to_string()
&[u8] 1String::from_utf8_lossy(x).to_string()
If you have x of type …Use this …
StringCString::new(x)?
CStringx
OsStringCString::new(x.to_str()?)?
PathBufCString::new(x.to_str()?)?
Vec<u8> 1CString::new(x)?
&strCString::new(x)?
&CStrx.to_owned() i
&OsStrCString::new(x.to_os_string().into_string()?)?
&PathCString::new(x.to_str()?)?
&[u8] 1CString::new(Vec::from(x))?
*mut c_char 2unsafe { CString::from_raw(x) }
If you have x of type …Use this …
StringOsString::from(x) i
CStringOsString::from(x.to_str()?)
OsStringx
PathBufx.into_os_string()
Vec<u8> 1unsafe { OsString::from_encoded_bytes_unchecked(x) }
&strOsString::from(x) i
&CStrOsString::from(x.to_str()?)
&OsStrOsString::from(x) i
&Pathx.as_os_str().to_owned()
&[u8] 1unsafe { OsString::from_encoded_bytes_unchecked(x.to_vec()) }
If you have x of type …Use this …
StringPathBuf::from(x) i
CStringPathBuf::from(x.to_str()?)
OsStringPathBuf::from(x) i
PathBufx
Vec<u8> 1unsafe { PathBuf::from(OsString::from_encoded_bytes_unchecked(x)) }
&strPathBuf::from(x) i
&CStrPathBuf::from(x.to_str()?)
&OsStrPathBuf::from(x) i
&PathPathBuf::from(x) i
&[u8] 1unsafe { PathBuf::from(OsString::from_encoded_bytes_unchecked(x.to_vec())) }
If you have x of type …Use this …
Stringx.into_bytes()
CStringx.into_bytes()
OsStringx.into_encoded_bytes()
PathBufx.into_os_string().into_encoded_bytes()
Vec<u8> 1x
&strVec::from(x.as_bytes())
&CStrVec::from(x.to_bytes_with_nul())
&OsStrVec::from(x.as_encoded_bytes())
&PathVec::from(x.as_os_str().as_encoded_bytes())
&[u8] 1x.to_vec()
If you have x of type …Use this …
Stringx.as_str()
CStringx.to_str()?
OsStringx.to_str()?
PathBufx.to_str()?
Vec<u8> 1std::str::from_utf8(&x)?
&strx
&CStrx.to_str()?
&OsStrx.to_str()?
&Pathx.to_str()?
&[u8] 1std::str::from_utf8(x)?
If you have x of type …Use this …
StringCString::new(x)?.as_c_str()
CStringx.as_c_str()
OsStringx.to_str()?
PathBuf?,3
Vec<u8> 1,4CStr::from_bytes_with_nul(&x)?
&str?,3
&CStrx
&OsStr?
&Path?
&[u8] 1,4CStr::from_bytes_with_nul(x)?
*const c_char 1unsafe { CStr::from_ptr(x) }
If you have x of type …Use this …
StringOsStr::new(&x)
CString?
OsStringx.as_os_str()
PathBufx.as_os_str()
Vec<u8> 1unsafe { OsStr::from_encoded_bytes_unchecked(&x) }
&strOsStr::new(x)
&CStr?
&OsStrx
&Pathx.as_os_str()
&[u8] 1unsafe { OsStr::from_encoded_bytes_unchecked(x) }
If you have x of type …Use this …
StringPath::new(x) r
CStringPath::new(x.to_str()?)
OsStringPath::new(x.to_str()?) r
PathBufPath::new(x.to_str()?) r
Vec<u8> 1unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(&x)) }
&strPath::new(x) r
&CStrPath::new(x.to_str()?)
&OsStrPath::new(x) r
&Pathx
&[u8] 1unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(x)) }
If you have x of type …Use this …
Stringx.as_bytes()
CStringx.as_bytes()
OsStringx.as_encoded_bytes()
PathBufx.as_os_str().as_encoded_bytes()
Vec<u8> 1&x
&strx.as_bytes()
&CStrx.to_bytes_with_nul()
&OsStrx.as_encoded_bytes()
&Pathx.as_os_str().as_encoded_bytes()
&[u8] 1x
You wantAnd have xUse this …
*const c_charCStringx.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 must ensure x comes with a valid representation for the string type (e.g., UTF-8 data for a String).
2 The c_char must have come from a previous CString. If it comes from FFI see &CStr instead.
3 No known shorthand as x will lack terminating 0x0. Best way to probably go via CString.
4 Must ensure x actually ends with 0x0.

String Outputurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

How to convert types into a String, or output them.

Rust has, among others, these APIs to convert types to stringified output, collectively called format macros:

MacroOutputNotes
format!(fmt)StringBread-and-butter "to String" converter.
print!(fmt)ConsoleWrites to standard output.
println!(fmt)ConsoleWrites to standard output.
eprint!(fmt)ConsoleWrites to standard error.
eprintln!(fmt)ConsoleWrites to standard error.
write!(dst, fmt)BufferDon't forget to also use std::io::Write;
writeln!(dst, fmt)BufferDon't forget to also use std::io::Write;
 
MethodNotes
x.to_string() STDProduces String, implemented for any Display type.
 

Here fmt is string literal such as "hello {}", that specifies output (compare "Formatting" tab) and additional parameters.

In format! and friends, types convert via trait Display "{}" STD or Debug "{:?}" STD , non exhaustive list:

TypeImplements
StringDebug, Display
CStringDebug
OsStringDebug
PathBufDebug
Vec<u8>Debug
&strDebug, Display
&CStrDebug
&OsStrDebug
&PathDebug
&[u8]Debug
boolDebug, Display
charDebug, Display
u8i128Debug, Display
f32, f64Debug, Display
!Debug, Display
()Debug
 

In short, pretty much everything is Debug; more special types might need special handling or conversion to Display.

Each argument designator in format macro is either empty {}, {argument}, or follows a basic syntax:

{ [argument] ':' [[fill] align] [sign] ['#'] [width [$]] ['.' precision [$]] [type] }
ElementMeaning
argumentNumber (0, 1, …), variable '21 or name,'18 e.g., print!("{x}").
fillThe character to fill empty spaces with (e.g., 0), if width is specified.
alignLeft (<), center (^), or right (>), if width is specified.
signCan be + for sign to always be printed.
#Alternate formatting, e.g., prettify DebugSTD formatter ? or prefix hex with 0x.
widthMinimum width (≥ 0), padding with fill (default to space). If starts with 0, zero-padded.
precisionDecimal digits (≥ 0) for numerics, or max width for non-numerics.
$Interpret width or precision as argument identifier instead to allow for dynamic formatting.
typeDebugSTD (?) formatting, hex (x), binary (b), octal (o), pointer (p), exp (e) … see more.
 
Format ExampleExplanation
{}Print the next argument using Display.STD
{x}Same, but use variable x from scope. '21
{:?}Print the next argument using Debug.STD
{2:#?}Pretty-print the 3rd argument with DebugSTD formatting.
{val:^2$}Center the val named argument, width specified by the 3rd argument.
{:<10.3}Left align with width 10 and a precision of 3.
{val:#x}Format val argument as hex, with a leading 0x (alternate format for x).
 
Full ExampleExplanation
println!("{}", x)Print x using DisplaySTD on std. out and append new line. '15 🗑️
println!("{x}")Same, but use variable x from scope. '21
format!("{a:.3} {b:?}")Convert a with 3 digits, add space, b with Debug STD, return String. '21
 

Toolingurl

Project Anatomyurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

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

EntryCode
📁 .cargo/Project-local cargo configuration, may contain config.toml. 🔗 🧠
📁 benches/Benchmarks for your crate, run via cargo bench, requires nightly by default. * 🚧
📁 examples/Examples how to use your crate, they see your crate like external user would.
          my_example.rsIndividual examples are run like cargo run --example my_example.
📁 src/Actual source code for your project.
          main.rsDefault entry point for applications, this is what cargo run uses.
          lib.rsDefault entry point for libraries. This is where lookup for my_crate::f() starts.
📁 src/bin/Place for additional binaries, even in library projects.
          extra.rsAdditional binary, run with cargo run --bin extra.
📁 tests/Integration tests go here, invoked via cargo test. Unit tests often stay in src/ file.
.rustfmt.tomlIn case you want to customize how cargo fmt works.
.clippy.tomlSpecial configuration for certain clippy lints, utilized via cargo clippy 🧠
build.rsPre-build script, 🔗 useful when compiling C / FFI, …
Cargo.tomlMain project manifest, 🔗 Defines dependencies, artifacts …
Cargo.lockFor reproducible builds. Add to git for apps, consider not for libs. 💬 🔗 🔗
rust-toolchain.tomlDefine toolchain override🔗 (channel, components, targets) for this project.

* On stable consider Criterion.

 

Minimal examples for various entry points might look like:

// src/main.rs (default application entry point)

fn main() {
    println!("Hello, world!");
}
// 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.
// 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);
    }
}
// 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.
// 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.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.

// 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]   // Crates can now use `#[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:

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! 🛑

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();2

1 Counts in Types and in Functions, defines type X and constant X.
2 Counts in Types and in Functions, defines type X and function X.

  • In any given scope, for example within a module, only one item 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.

 

Cargourl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Commands and tools that are good to know.

CommandDescription
cargo initCreate a new project for the latest edition.
cargo buildBuild the project in debug mode (--release for all optimization).
cargo checkCheck if project would compile (much faster).
cargo testRun tests for the project.
cargo doc --no-deps --openLocally generate documentation for your code.
cargo runRun your project, if a binary is produced (main.rs).
     cargo run --bin bRun binary b. Unifies feat. with other dependents (can be confusing).
     cargo run --package wRun main of sub-worksp. w. Treats features more sanely.
cargo … --timingsShow what crates caused your build to take so long. 🔥
cargo treeShow dependency graph, all crates used by project, transitively.
     cargo tree -i fooInverse dependency lookup, explain why foo is used.
cargo info fooShow crate metadata for foo (by default for version used by this project).
cargo +{nightly, stable}Use given toolchain for command, e.g., for 'nightly only' tools.
cargo +nightly …Some nightly-only commands (substitute with command below)
     rustc -- -Zunpretty=expandedShow expanded macros. 🚧
rustup docOpen offline Rust documentation (incl. the books), good on a plane!

Here cargo build means you can either type cargo build or just cargo b; and --release means it can be replaced with -r.

 

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

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

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

 

Cross Compilationurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

🔘 Check target is supported.

🔘 Install target via rustup target install aarch64-linux-android (for example).

🔘 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 CXX=C:\[PATH_TO_TOOLCHAIN]\prebuilt\windows-x86_64\bin\aarch64-linux-android21-clang.cmd
set AR=C:\[PATH_TO_TOOLCHAIN]\prebuilt\windows-x86_64\bin\aarch64-linux-android-ar.exe
…

Whether you set them depends on how compiler complains, not necessarily all are needed.

Some platforms / configurations can be extremely sensitive how paths are specified (e.g., \ vs /) and quoted.

✔️ Compile with cargo build --target=aarch64-linux-android

 

Tooling Directivesurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

特殊 标记 参考

Special tokens embedded in source code used by tooling or preprocessing.

可在代码中使用的特殊 "标记", 以实现一定功能或预处理(代码).

Inside a declarative BK macro by example BK EX REF macro_rules! implementation these fragment specifiers REF work:

Within MacrosExplanation
$x:tyMacro capture (here a $x is the capture and ty means x must be type).
     $x:blockA block {} of statements or expressions, e.g., { let x = 5; }
     $x:exprAn expression, e.g., x, 1 + 1, String::new() or vec![]
     $x:expr_2021An expression that matches the behavior of Rust '21 RFC
     $x:identAn identifier, for example in let x = 0; the identifier is x.
     $x:itemAn item, like a function, struct, module, etc.
     $x:lifetimeA lifetime (e.g., 'a, 'static, etc.).
     $x:literalA literal (e.g., 3, "foo", b"bar", etc.).
     $x:metaA meta item; the things that go inside #[…] and #![…] attributes.
     $x:patA pattern, e.g., Some(t), (17, 'a') or _.
     $x:pathA path (e.g., foo, ::std::mem::replace, transmute::<_, int>).
     $x:stmtA statement, e.g., let x = 1 + 1;, String::new(); or vec![];
     $x:ttA single token tree, see here for more details.
     $x:tyA type, e.g., String, usize or Vec<u8>.
     $x:visA visibility modifier; pub, pub(crate), etc.
$crateSpecial hygiene variable, crate where macros is defined. ?

Inside a doc comment BK EX REF these work:

Within Doc CommentsExplanation
```…```Include a doc test (doc code running on cargo test).
```X,Y …```Same, and include optional configurations; with X, Y being …
     rustMake it explicit test is written in Rust; implied by Rust tooling.
     -Compile test. Run test. Fail if panic. Default behavior.
     should_panicCompile test. Run test. Execution should panic. If not, fail test.
     no_runCompile test. Fail test if code can't be compiled, Don't run test.
     compile_failCompile test but fail test if code can be compiled.
     ignoreDo not compile. Do not run. Prefer option above instead.
     edition2018Execute code as Rust '18; default is '15.
#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.

Attributes affecting the whole crate or app:

Opt-Out'sOnExplanation
#![no_std]CDon't (automatically) import stdSTD ; use coreSTD instead. REF
#![no_implicit_prelude]CMDon't add preludeSTD, need to manually import None, Vec, … REF
#![no_main]CDon't emit main() in apps if you do that yourself. REF
 
Opt-In'sOnExplanation
#![feature(a, b, c)]CRely on f. that may not get stabilized, c. Unstable Book. 🚧
 
BuildsOnExplanation
#![crate_name = "x"]CSpecify current crate name, e.g., when not using cargo. ? REF 🧠
#![crate_type = "bin"]CSpecify current crate type (bin, lib, dylib, cdylib, …). REF 🧠
#![recursion_limit = "123"]CSet compile-time recursion limit for deref, macros, … REF 🧠
#![type_length_limit = "456"]CLimits maximum number of type substitutions. REF 🧠
#![windows_subsystem = "x"]COn Windows, make a console or windows app. REF 🧠
 
HandlersOnExplanation
#[alloc_error_handler]FMake some fn(Layout) -> ! the allocation fail. handler. 🔗 🚧
#[global_allocator]SMake static item impl. GlobalAlloc STD global allocator. REF
#[panic_handler]FMake some fn(&PanicInfo) -> ! app's panic handler. REF

Attributes primarily governing emitted code:

Developer UXOnExplanation
#[non_exhaustive]TFuture-proof struct or enum; hint it may grow in future. REF
#[path = "x.rs"]MGet module from non-standard file. REF
#[diagnostic::on_unimplemented]XGive better error messages when trait not implemented. RFC
 
CodegenOnExplanation
#[cold]FHint that function probably isn't going to be called. REF
#[inline]FNicely suggest compiler should inline function at call sites. REF
#[inline(always)]FEmphatically threaten compiler to inline call, or else. REF
#[inline(never)]FInstruct compiler to feel sad if it still inlines the function. REF
#[repr(X)]1TUse another representation instead of the default rust REF one:
#[target_feature(enable="x")]FEnable CPU feature (e.g., avx2) for code of unsafe fn. REF
#[track_caller]FAllows fn to find callerSTD for better panic messages. REF
     #[repr(C)]TUse a C-compatible (f. FFI), predictable (f. transmute) layout. REF
     #[repr(C, u8)]enumGive enum discriminant the specified type. REF
     #[repr(transparent)]TGive single-element type same layout as contained field. REF
     #[repr(packed(1))]TLower align. of struct and contained fields, mildly UB prone. REF
     #[repr(align(8))]TRaise alignment of struct to given value, e.g., for SIMD types. REF

1 Some representation modifiers can be combined, e.g., #[repr(C, packed(1))].

 
LinkingOnExplanation
#[unsafe(no_mangle)]*Use item name directly as symbol name, instead of mangling. REF
#[unsafe(export_name = "foo")]FSExport a fn or static under a different name. REF
#[unsafe(link_section = ".x")]FSSection name of object file where item should be placed. REF
#[link(name="x", kind="y")]XNative lib to link against when looking up symbol. REF
#[link_name = "foo"]FName of symbol to search for resolving extern fn. REF
#[no_link]XDon't link extern crate when only wanting macros. REF
#[used]SDon't optimize away static variable despite it looking unused. REF

Attributes used by Rust tools to improve code quality:

Code PatternsOnExplanation
#[allow(X)]*Instruct rustc / clippy to ign. class X of possible issues. REF
#[expect(X)] 1*Warn if a lint doesn't trigger. 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 = "msg"]*Let your users know you made a design mistake. REF
#[must_use = "msg"]FTXMakes compiler check return value is processed by 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).

 
TestsOnExplanation
#[test]FMarks the function as a test, run with cargo test. 🔥 REF
#[ignore = "msg"]FCompiles but does not execute some #[test] for now. REF
#[should_panic]FTest must panic!() to actually succeed. REF
#[bench]FMark function in bench/ as benchmark for cargo bench. 🚧 REF
 
FormattingOnExplanation
#[rustfmt::skip]*Prevent cargo fmt from cleaning up item. 🔗
#![rustfmt::skip::macros(x)]CM… from cleaning up macro x. 🔗
#![rustfmt::skip::attributes(x)]CM… from cleaning up attribute x. 🔗
 
DocumentationOnExplanation
#[doc = "Explanation"]*Same as adding a /// doc comment. 🔗
#[doc(alias = "other")]*Provide other name for search in docs. 🔗
#[doc(hidden)]*Prevent item from showing up in docs. 🔗
#![doc(html_favicon_url = "")]CSets the favicon for the docs. 🔗
#![doc(html_logo_url = "")]CThe logo used in the docs. 🔗
#![doc(html_playground_url = "")]CGenerates Run buttons and uses given service. 🔗
#![doc(html_root_url = "")]CBase URL for links to external crates. 🔗
#![doc(html_no_source)]CPrevents source from being included in docs. 🔗

Attributes related to the creation and use of macros:

Macros By ExampleOnExplanation
#[macro_export]!Export macro_rules! as pub on crate level REF
#[macro_use]MXLet macros persist past mod.; or import from extern crate. REF
 
Proc MacrosOnExplanation
#[proc_macro]FMark fn as function-like procedural m. callable as m!(). REF
#[proc_macro_derive(Foo)]FMark fn as derive macro which can #[derive(Foo)]. REF
#[proc_macro_attribute]FMark fn as attribute macro for new #[x]. REF
 
DerivesOnExplanation
#[derive(X)]TLet some proc macro provide a goodish impl of trait X. 🔥 REF

Attributes governing conditional compilation:

Config AttributesOnExplanation
#[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))]*Include item if X does not hold. REF
#[cfg_attr(X, foo = "msg")]*Apply #[foo = "msg"] if configuration X 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 OptionsOnExplanation
#[cfg(debug_assertions)]*Whether debug_assert!() & co. would panic. REF
#[cfg(feature = "foo")]*When your crate was compiled with f. foo. 🔥 REF
#[cfg(target_arch = "x86_64")]*The CPU architecture crate is compiled for. REF
#[cfg(target_env = "msvc")]*How DLLs and functions are interf. with on OS. REF
#[cfg(target_endian = "little")]*Main reason your new zero-cost prot. fails. REF
#[cfg(target_family = "unix")]*Family operating system belongs to. REF
#[cfg(target_feature = "avx")]*Whether a particular class of instructions is avail. REF
#[cfg(target_os = "macos")]*Operating system your code will run on. REF
#[cfg(target_pointer_width = "64")]*How many bits ptrs, usize and words have. REF
#[cfg(target_vendor = "apple")]*Manufacturer of target. REF
#[cfg(panic = "unwind")]*Whether unwind or abort will happen on panic. ?
#[cfg(proc_macro)]*Whether crate compiled as proc macro. REF
#[cfg(test)]*Whether compiled with cargo test. 🔥 REF

Environment variables and outputs related to the pre-build script. Consider build-rs🔗 instead.

Input EnvironmentExplanation 🔗
CARGO_FEATURE_XEnvironment variable set for each feature x activated.
     CARGO_FEATURE_SOMETHINGIf feature something were enabled.
     CARGO_FEATURE_SOME_FEATUREIf f. some-feature were enabled; dash - converted to _.
CARGO_CFG_XExposes cfg's; joins mult. opts. by , and converts - to _.
     CARGO_CFG_TARGET_OS=macosIf target_os were set to macos.
     CARGO_CFG_TARGET_FEATURE=avx,avx2If target_feature were set to avx and avx2.
OUT_DIRWhere output should be placed.
TARGETTarget triple being compiled for.
HOSTHost triple (running this build script).
PROFILECan be debug or release.

Available in build.rs via env::var()?. List not exhaustive.

 
Output StringExplanation 🔗
cargo::rerun-if-changed=PATH(Only) run this build.rs again if PATH changed.
cargo::rerun-if-env-changed=VAR(Only) run this build.rs again if environment VAR changed.
cargo::rustc-cfg=KEY[="VALUE"]Emit given cfg option to be used for later compilation.
cargo::rustc-cdylib-link-arg=FLAG When building a cdylib, pass linker flag.
cargo::rustc-env=VAR=VALUE Emit var accessible via env!() in crate during compilation.
cargo::rustc-flags=FLAGSAdd special flags to compiler. ?
cargo::rustc-link-lib=[KIND=]NAMELink native library as if via -l option.
cargo::rustc-link-search=[KIND=]PATHSearch path for native library as if via -L option.
cargo::warning=MESSAGEEmit 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.


Working with Typesurl

Types, Traits, Genericsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Allowing users to bring their own types and avoid code duplication.

Types
u8 String Device
  • Set of values with given semantics, layout, …
TypeValues
u8{ 0u8, 1u8,, 255u8 }
char{ 'a', 'b', … '🦀' }
struct S(u8, char){ (0u8, 'a'),(255u8, '🦀') }

Sample types and sample values.

Type Equivalence and Conversions
u8 &u8 &mut u8 [u8; 1] String
  • It may be obvious but   u8,    &u8,    &mut u8, are entirely different from each other
  • Any t: T only accepts values from exactly T, e.g.,
    • f(0_u8) can't be called with f(&0_u8),
    • f(&mut my_u8) can't be called with f(&my_u8),
    • f(0_u8) can't be called with f(0_i8).

Yes, 0 != 0 (in a mathematical sense) when it comes to types! In a language sense, the operation ==(0u8, 0u16) just isn't defined to prevent happy little accidents.

TypeValues
u8{ 0u8, 1u8,, 255u8 }
u16{ 0u16, 1u16,, 65_535u16 }
&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 types1
    • casts manually convert values of types, 0_i8 as u8
    • coercions automatically convert types if safe2, 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 inherent implementations, REF e.g., impl Port {}, behavior related to type:
    • associated functions Port::new(80)
    • methods port.close()

What's considered related is more philosophical than technical, nothing (except good taste) would prevent a u8::play_sound() from happening.

Traits — trait T { }
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 membership 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. ?

Type Constructors — Vec<>
Vec<u8> Vec<char>
  • Vec<u8> is type "vector of bytes"; Vec<char> is type "vector of chars", but what is Vec<>?
ConstructValues
Vec<u8>{ [], [1], [1, 2, 3],}
Vec<char>{ [], ['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 3rd party to construct concrete type via parameter,
    • only then would this Vec<UserType> become real type itself.
Generic Parameters — <T>
Vec<T> [T; 128] &T &mut T S<T>
  • Parameter for Vec<> often named T therefore Vec<T>.
  • T "variable name for type" for user to plug in something specific, Vec<f32>, S<u8>, …
Type ConstructorProduces Family
struct Vec<T> {}Vec<u8>, Vec<f32>, Vec<Vec<u8>>, …
[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<T> {
    x: T
}

// Within 'concrete' code an existing type must be given for T.
fn f() {
    let x: S<f32> = S::new(0_f32);
}

Const Generics — [T; N] and S<const N: usize>
[T; n] S<const N>
  • 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<T, const N: usize>.
Type ConstructorProduces Family
[u8; N][u8; 0], [u8; 1], [u8; 2], …
struct S<const N: usize> {}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<T, const N: usize> {
    data: [T; N],
}
Bounds (Simple) — where T: X
🧔 Num<T> 🎅 Num<u8> Num<f32> Num<Cmplx> u8 Absolute Dim Mul Port Clone ShowHex
  • If T can be any type, how can we reason about (write code) for such a Num<T>?
  • Parameter bounds:
    • limit what types (trait bound) or values (const bound ?) allowed,
    • we now can make use of these limits!
  • Trait bounds act as "membership check":
// Type can only be constructed for some `T` if that
// T is part of `Absolute` membership list.
struct Num<T> 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<T>
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<T> S<T> 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 <T> part),
  • where that type must be member of the Absolute + Dim + Mul traits,
  • you may add an implementation block to the type family S<>,
  • containing the methods …

You can think of such impl<T>{} code as abstractly implementing a family of behaviors. REF Most notably, they allow 3rd parties to transparently materialize implementations similarly to how type constructors materialize types:

// 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<T> 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<T> 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.

Trait Parameters — Trait<In> { type Out; }
 

Notice how some traits can be "attached" multiple times, but others just once?

Port From<u8> From<u16> Port Deref type u8;
 

Why is that?

  • Traits themselves can be generic over two kinds of parameters:
    • trait From<I> {}
    • 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<u8> for u16 {}
impl From<u16> for u32 {}
impl Deref for Port { type O = u8; }
impl Deref for String { type O = str; }
From
SelfI
u16u8
u32u16
Deref
SelfO
Portu8
Stringstr

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<I1, I2> {
    type O1;
    type O2;
}
  • this creates a 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]I1I2O1O2
Playeru8charf32f32
EvilMonsteru16stru8u8
EvilMonsteru16Stringu8u8
NiceMonsteru16Stringu8u8
NiceMonster🛑u16Stringu8u16

Various trait implementations. The last one is not valid as (NiceMonster, u16, String) has
already uniquely determined the outputs.

Trait Authoring Considerations (Abstract)
👩‍🦰 A<I> 🧔 Car 👩‍🦰 / 🧔 Car A<I> 🎅 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<I> { }
trait B { type O; }

// Implementor adds (X, u32) to A.
impl A<u32> for X { }

// Implementor adds family impl. (X, …) to A, user can materialze.
impl<T> A<T> for Y { }

// Implementor must decide specific entry (X, O) added to B.
impl B for X { type O = u32; }
A
SelfI
Xu32
Y

Santa may add more members by providing his own type for T.

B
SelfO
PlayerString
Xu32

For given set of inputs (here Self), implementor must pre-select O.

Trait Authoring Considerations (Example)
Query vs. Query<I> vs. Query type O; vs. Query<I> type O;
 

Choice of parameters goes along with purpose trait has to fill.


No Additional Parameters

trait Query {
    fn search(&self, needle: &str);
}

impl Query for PostgreSQL {}
impl Query for Sled {}

postgres.search("SELECT …");
👩‍🦰 Query 🧔 PostgreSQL Query Sled Query
 

Trait author assumes:

  • neither implementor nor user need to customize API.
 

Input Parameters

trait Query<I> {
    fn search(&self, needle: I);
}

impl Query<&str> for PostgreSQL {}
impl Query<String> for PostgreSQL {}
impl<T> Query<T> for Sled where T: ToU8Slice {}

postgres.search("SELECT …");
postgres.search(input.to_string());
sled.search(file);
👩‍🦰 Query<I> 🧔 PostgreSQL Query<&str> Query<String> Sled Query<T> where T is ToU8Slice.
 

Trait author assumes:

  • implementor would customize API in multiple ways for same Self type,
  • users may want ability to decide for which I-types behavior should be possible.
 

Output Parameters

trait Query {
    type O;
    fn search(&self, needle: Self::O);
}

impl Query for PostgreSQL { type O = String;}
impl Query for Sled { type O = Vec<u8>;}

postgres.search("SELECT …".to_string());
sled.search(vec![0, 1, 2, 4]);
👩‍🦰 Query type O; 🧔 PostgreSQL Query O = String; Sled Query O = Vec<u8>;
 

Trait author assumes:

  • implementor would customize API for Self type (but in only one way),
  • users do not need, or should not have, ability to influence customization for specific Self.

As you can see here, the term input or output does not (necessarily) have anything to do with whether I or O are inputs or outputs to an actual function!

 

Multiple In- and Output Parameters

trait Query<I> {
    type O;
    fn search(&self, needle: I) -> Self::O;
}

impl Query<&str> for PostgreSQL { type O = String;}
impl Query<CString> for PostgreSQL { type O = CString;}
impl<T> Query<T> for Sled where T: ToU8Slice { type O = Vec<u8>;}

postgres.search("SELECT …").to_uppercase();
sled.search(&[1, 2, 3, 4]).pop();
👩‍🦰 Query<I> type O; 🧔 PostgreSQL Query<&str> O = String; Query<CString> O = CString; Sled Query<T> O = Vec<u8>; where T is ToU8Slice.
 

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, implementor should determine resulting output type.
Dynamic / Zero Sized Types
MostTypes Sized Normal types. vs. Z Sized Zero sized. vs. str Sized Dynamically sized. [u8] Sized dyn Trait Sized Sized
  • A type T is Sized STD if at compile time it is known how many bytes it occupies, u8 and &[u8] are, [u8] isn't.
  • Being Sized means impl Sized for T {} holds. Happens automatically and cannot be user impl'ed.
  • Types not Sized are called dynamically sized types BK NOM REF (DSTs), sometimes unsized.
  • Types without data are called zero sized types NOM (ZSTs), do not occupy space.
Example 实例Explanation 解释
struct A { x: u8 }Type A is sized, i.e., impl Sized for A holds, this is a 'regular' type.
struct B { x: [u8] }Since [u8] is a DST, B in turn becomes DST, i.e., does not impl Sized.
struct C<T> { x: T }Type params have implicit T: Sized bound, e.g., C<A> is valid, C<B> is not.
struct D<T: ?Sized> { x: T }Using ?Sized REF allows opt-out of that bound, i.e., D<B> is also valid.
struct E;Type E is zero-sized (and also sized) and will not consume memory.
trait F { fn f(&self); }Traits do not have an implicit Sized bound, i.e., impl F for B {} is valid.
     trait F: Sized {}Traits can however opt into Sized via supertraits.
trait G { fn g(self); }For Self-like params DST impl may still fail as params can't go on stack.
?Sized
S<T> S<u8> S<char> S<str>
struct S<T> {}
  • T can be any concrete type.
  • However, there exists invisible default bound T: Sized, so S<str> is not possible out of box.
  • Instead we have to add T : ?Sized to opt-out of that bound:
S<T> S<u8> S<char> S<str>
struct S<T> where T: ?Sized {}
Generics and Lifetimes — <'a>
S<'a> &'a f32 &'a mut u8
  • Lifetimes act* as type parameters:
    • user must provide specific 'a to instantiate type (compiler will help within methods),
    • S<'p> and S<'q> are different types, just like Vec<f32> and Vec<u8> are
    • meaning you can't just assign value of type S<'a> to variable expecting S<'b> (exception: subtype relationship for lifetimes, i.e., 'a outlives 'b).
S<'a> S<'auto> S<'static>
  • 'static is only globally available type of the lifetimes kind.
// `'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. 🔗

Examples expand by clicking.

Foreign Types and Traitsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

A visual overview of types and traits in your crate and upstream.

u8 u16 f32 bool char File String Builder Vec<T> Vec<T> Vec<T> &'a T &'a T &'a T &mut 'a T &mut 'a T &mut 'a T [T; n] [T; n] [T; n] Vec<T> Vec<T> f<T>() {} drop() {} PI dbg! Copy Deref type Tgt; From<T> From<T> From<T> Items defined in upstream crates. Serialize Transport ShowHex Device From<u8> Foreign trait impl. for local type. String Serialize Local trait impl. for foreign type. String From<u8> 🛑 Illegal, foreign trait for f. type. String From<Port> Exception: Legal if used type local. Port From<u8> From<u16> 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.

Examples of traits and types, and which traits you can implement for which type.

Type Conversionsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

How to get B when you have A?

fn f(x: A) -> B {
    // How can you obtain B from A?
}
MethodExplanation
IdentityTrivial case, B is exactly A.
ComputationCreate and manipulate instance of B by writing code transforming data.
CastsOn-demand conversion between types where caution is advised.
CoercionsAutomatic conversion within 'weakening ruleset'.1
SubtypingAutomatic conversion within 'same-layout-different-lifetimes ruleset'.1
 

1 While both convert A to B, coercions generally link to an unrelated B (a type "one could reasonably expect to have different methods"), while subtyping links to a B differing only in lifetimes.

fn f(x: A) -> B {
    x.into()
}

Bread and butter way to get B from A. Some traits provide canonical, user-computable type relations:

TraitExampleTrait implies …
impl From<A> for B {}a.into()Obvious, always-valid relation.
impl TryFrom<A> for B {}a.try_into()?Obvious, sometimes-valid relation.
impl Deref for A {}*aA is smart pointer carrying B; also enables coercions.
impl AsRef<B> for A {}a.as_ref()A can be viewed as B.
impl AsMut<B> for A {}a.as_mut()A can be mutably viewed as B.
impl Borrow<B> for A {}a.borrow()A has borrowed analog B (behaving same under Eq, …).
impl ToOwned for A {}a.to_owned()A has owned analog B.
fn f(x: A) -> B {
    x as B
}

Convert types with keyword as if conversion relatively obvious but might cause issues. NOM

ABExample 实例Explanation 解释
PointerPointerdevice_ptr as *const u8If *A, *B are Sized.
PointerIntegerdevice_ptr as usize
IntegerPointermy_usize as *const Device
NumberNumbermy_u8 as u16Often surprising behavior.
enum w/o fieldsIntegerE::A as u8
boolIntegertrue as u8
charInteger'A' as u8
&[T; N]*const Tmy_ref as *const u8
fn()Pointerf as *const u8If Pointer is Sized.
fn()Integerf as usize
 

Where Pointer, Integer, Number are just used for brevity and actually mean:

  • Pointer any *const T or *mut T;
  • Integer any countable u8i128;
  • Number any Integer, f32, f64.

Opinion 💬 — Casts, esp. Number - Number, can easily go wrong. If you are concerned with correctness, consider more explicit methods instead.

fn f(x: A) -> B {
    x
}

Automatically weaken type A to B; types can be substantially1 different. NOM

ABExplanation
&mut T&TPointer weakening.
&mut T*mut T-
&T*const T-
*mut T*const T-
&T&UDeref, if impl Deref<Target=U> for T.
TUUnsizing, if impl CoerceUnsized<U> for T.2 🚧
TVTransitivity, if T coerces to U and U to V.
|x| x + xfn(u8) -> u8Non-capturing closure, to equivalent fn pointer.
 

1 Substantially meaning one can regularly expect a coercion result B to be an entirely different type (i.e., have entirely different methods) than the original type A.

2 Does not quite work in example above as unsized can't be on stack; imagine f(x: &A) -> &B instead. Unsizing works by default for:

  • [T; n] to [T]
  • T to dyn Trait if impl Trait for T {}.
  • Foo<, T,> to Foo<, U,> under arcane 🔗 circumstances.
fn f(x: A) -> B {
    x
}

Automatically converts A to B for types only differing in lifetimes NOM - subtyping examples:

A(subtype)B(supertype)Explanation
&'static u8&'a u8Valid, forever-pointer is also transient-pointer.
&'a u8&'static u8🛑 Invalid, transient should not be forever.
&'a &'b u8&'a &'b u8Valid, same thing. But now things get interesting. Read on.
&'a &'static u8&'a &'b u8Valid, &'static u8 is also &'b u8; covariant inside &.
&'a mut &'static u8&'a mut &'b u8🛑 Invalid and surprising; invariant inside &mut.
Box<&'static u8>Box<&'a u8>Valid, Box with forever is also box with transient; covariant.
Box<&'a u8>Box<&'static u8>🛑 Invalid, Box with transient may not be with forever.
Box<&'a mut u8>Box<&'a u8>🛑 Invalid, see table below, &mut u8 never was a &u8.
Cell<&'static u8>Cell<&'a u8>🛑 Invalid, Cell are never something else; invariant.
fn(&'static u8)fn(&'a u8)🛑 If fn needs forever it may choke on transients; contravar.
fn(&'a u8)fn(&'static u8)But sth. that eats transients can be(!) sth. that eats forevers.
for<'r> fn(&'r u8)fn(&'a u8)Higher-ranked type for<'r> fn(&'r u8) is also fn(&'a u8).
 

In contrast, these are not🛑 examples of subtyping:

ABExplanation
u16u8🛑 Obviously invalid; u16 should never automatically be u8.
u8u16🛑 Invalid by design; types w. different data still never subtype even if they could.
&'a mut u8&'a u8🛑 Trojan horse, not subtyping; but coercion (still works, just not subtyping).
 
fn f(x: A) -> B {
    x
}

Automatically converts A to B for types only differing in lifetimes NOM - subtyping variance rules:

  • A longer lifetime 'a that outlives a shorter 'b is a subtype of 'b.
  • Implies 'static is subtype of all other lifetimes 'a.
  • Whether types with parameters (e.g., &'a T) are subtypes of each other the following variance table is used:
Construct1'aTU
&'a Tcovariantcovariant
&'a mut Tcovariantinvariant
Box<T>covariant
Cell<T>invariant
fn(T) -> Ucontravariantcovariant
*const Tcovariant
*mut Tinvariant

Covariant means if A is subtype of B, then T[A] is subtype of T[B].
Contravariant means if A is subtype of B, then T[B] is subtype of T[A].
Invariant means even if A is subtype of B, neither T[A] nor T[B] will be subtype of the other.

1 Compounds like struct S<T> {} obtain variance through their used fields, usually becoming invariant if multiple variances are mixed.

💡 In other words, 'regular' types are never subtypes of each other (e.g., u8 is not subtype of u16), and a Box<u32> would never be sub- or supertype of anything. However, generally a Box<A>, can be subtype of Box<B> (via covariance) if A is a subtype of B, which can only happen if A and B are 'sort of the same type that only differed in lifetimes', e.g., A being &'static u32 and B being &'a u32.

 

Coding Guidesurl

Idiomatic Rusturl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

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

IdiomCode
Think in Expressionsy = if x { a } else { b };
y = loop { break 5 };
fn f() -> u32 { 0 }
Think in Iterators(1..10).map(f).collect()
names.iter().filter(|x| x.starts_with("A"))
Test Absence with ?y = try_something()?;
get_option()?.run()?
Use Strong Typesenum E { Invalid, Valid {} } over ERROR_INVALID = -1
enum E { Visible, Hidden } over visible: bool
struct Charge(f32) over f32
Illegal State: Impossiblemy_lock.write().unwrap().guaranteed_at_compile_time_to_be_locked = 10; 1
thread::scope(|s| { /* Threads can't exist longer than scope() */ });
Avoid Global StateBeing depended on in multiple versions can secretly duplicate statics. 🛑 🔗
Provide BuildersCar::new("Model T").hp(20).build();
Make it ConstWhere possible mark fns. const; where feasible run code inside const {}.
Don't PanicPanics are not exceptions, they suggest immediate process abortion!
Only panic on programming error; use Option<T>STD or Result<T,E>STD otherwise.
If clearly user requested, e.g., calling obtain() vs. try_obtain(), panic ok too.
Inside const { NonZero::new(1).unwrap() } p. becomes compile error, ok too.
Generics in ModerationA simple <T: Bound> (e.g., AsRef<Path>) can make your APIs nicer to use.
Complex bounds make it impossible to follow. If in doubt don't be creative with g.
Split ImplementationsGenerics like Point<T> can have separate impl per T for some specialization.
impl<T> Point<T> { /* Add common methods here */ }
impl Point<f32> { /* Add methods only relevant for Point<f32> */ }
UnsafeAvoid unsafe {}, often safer, faster solution without it.
Implement Traits#[derive(Debug, Copy, …)] and custom impl where needed.
ToolingRun clippy regularly to significantly improve your code quality. 🔥
Format your code with rustfmt for consistency. 🔥
Add unit tests BK (#[test]) to ensure your code works.
Add doc tests BK (``` my_api::f() ```) to ensure docs match code.
DocumentationAnnotate your APIs with doc comments that can show up on docs.rs.
Don't forget to include a summary sentence and the Examples heading.
If applicable: Panics, Errors, Safety, Abort and Undefined Behavior.

1 In most cases you should prefer ? over .unwrap(). In the case of locks however the returned PoisonError signifies a panic in another thread, so unwrapping it (thus propagating the panic) is often the better idea.

 

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

 

Performance Tipsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

"My code is slow" sometimes comes up when porting microbenchmarks to Rust, or after profiling.

RatingNameDescription
🚀🍼Release Mode BK 🔥Always do cargo build --release for massive speed boost.
🚀🍼🚀⚠️Target Native CPU 🔗Add rustflags = ["-Ctarget-cpu=native"] to config.toml.
🚀🍼⚖️Codegen Units 🔗Codegen units 1 may yield faster code, slower compile.
🚀🍼Reserve Capacity STDPre-allocation of collections reduces allocation pressure.
🚀🍼Recycle Collections STDCalling x.clear() and reusing x prevents allocations.
🚀🍼Append to Strings STDUsing write!(&mut s, "{}") can prevent extra allocation.
🚀🍼⚖️Global Allocator STDOn some platforms ext. allocator (e.g., mimalloc 🔗) faster.
Bump Allocations 🔗Cheaply gets temporary, dynamic memory, esp. in hot loops.
Batch APIsDesign APIs to handle multiple similar elements at once, e.g., slices.
🚀🚀⚖️SoA / AoSoA 🔗Beyond that consider struct of arrays (SoA) and similar.
🚀🚀⚖️SIMD STD 🚧Inside (math heavy) batch APIs using SIMD can give 2x - 8x boost.
Reduce Data SizeSmall types (e.g, u8 vs u32, niches?) and data have better cache use.
Keep Data Nearby 🔗Storing often-used data nearby can improve memory access times.
Pass by Size 🔗Small (2-3 words) structs best passed by value, larger by reference.
🚀🚀⚖️Async-Await 🔗If parallel waiting happens a lot (e.g., server I/O) async good idea.
Threading STDThreads allow you to perform parallel work on mult. items at once.
🚀... in appOften good for apps, as lower wait times means better UX.
🚀🚀⚖️... inside libsOpaque t. use inside lib often not good idea, can be too opinionated.
🚀🚀... for lib callersHowever, allowing your user to process you in parallel excellent idea.
🚀🚀⚖️Avoid LocksLocks in multi-threaded code kills parallelism.
🚀🚀⚖️Avoid AtomicsNeedless atomics (e.g., Arc vs Rc) impact other memory access.
🚀🚀⚖️Avoid False Sharing 🔗Make sure data R/W by different CPUs at least 64 bytes apart. 🔗
🚀🍼Buffered I/O STD 🔥Raw File I/O highly inefficient w/o buffering.
🚀🍼🚀⚠️Faster Hasher 🔗Default HashMap STD hasher DoS attack-resilient but slow.
🚀🍼🚀⚠️Faster RNGIf you use a crypto RNG consider swapping for non-crypto.
🚀🚀⚖️Avoid Trait Objects 🔗T.O. reduce code size, but increase memory indirection.
🚀🚀⚖️Defer Drop 🔗Dropping heavy objects in dump-thread can free up current one.
🚀🍼🚀⚠️Unchecked APIs STDIf you are 100% confident, unsafe { unchecked_ } skips checks.

Entries marked 🚀 often come with a massive (> 2x) performance boost, 🍼 are easy to implement even after-the-fact, ⚖️ might have costly side effects (e.g., memory, complexity), ⚠️ have special risks (e.g., security, correctness).

 

Profiling Tips 💬

Profilers are indispensable to identify hot spots in code. For the best experience add this to your Cargo.toml:

[profile.release]
debug = true

Then do a cargo build --release and run the result with Superluminal (Windows) or Instruments (macOS). That said, there are many performance opportunities profilers won't find, but that need to be designed in.

 

Async-Await 101url 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

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

ConstructExplanation
asyncAnything declared async always returns an impl Future<Output=_>. STD
     async fn f() {}Function f returns an impl Future<Output=()>.
     async fn f() -> S {}Function f returns an impl Future<Output=S>.
     async { x }Transforms { x } into an impl Future<Output=X>.
let sm = f(); Calling f() that is async will not execute f, but produce state machine sm. 1 2
     sm = async { g() };Likewise, does not execute the { g() } block; produces state machine.
runtime.block_on(sm);Outside an async {}, schedules sm to actually run. Would execute g(). 3 4
sm.awaitInside 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., tokio. Also, more helpers in futures crate.

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

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

Simplified diagram for code written inside an async block :

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

With the execution flow in mind, some considerations when writing code inside an async construct:

Constructs 1Explanation
sleep_or_block();Definitely bad 🛑, never halt current thread, clogs executor.
set_TL(a); x.await; TL();Definitely bad 🛑, await may return from other thread, thread local invalid.
s.no(); x.await; s.go();Maybe bad 🛑, await will not return if Future dropped while waiting. 2
Rc::new(); x.await; rc();Non-Send types prevent impl Future from being Send; less compatible.

1 Here we assume s is any non-local that could temporarily be put into an invalid state; TL is any thread local storage, and that the async {} containing the code is written without assuming executor specifics.
2 Since Drop is run in any case when Future is dropped, consider using drop guard that cleans up / fixes application state if it has to be left in bad condition across .await points.

 

Closures in APIsurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

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:

SignatureFunction g can call …Function g accepts …
g<F: FnOnce()>(f: F)f() at most once.Fn, FnMut, FnOnce
g<F: FnMut()>(mut f: F)f() multiple times.Fn, FnMut
g<F: Fn()>(f: F)f() multiple times.Fn

Notice how asking for a Fn closure as a function is most restrictive for the caller; but having a Fn closure as a caller is most compatible with any function.

 

From the perspective of someone defining a closure:

ClosureImplements*Comment
|| { moved_s; } FnOnceCaller must give up ownership of moved_s.
|| { &mut s; } FnOnce, FnMutAllows g() to change caller's local state s.
|| { &s; } FnOnce, FnMut, FnMay not mutate state; but can share and reuse s.

* Rust prefers capturing by reference (resulting in the most "compatible" Fn closures from a caller perspective), but can be forced to capture its environment by copy or move via the move || {} syntax.

 

That gives the following advantages and disadvantages:

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

Unsafe, Unsound, Undefinedurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

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

Safe Code

  • Safe has narrow meaning in Rust, vaguely 'the intrinsic prevention of undefined behavior (UB)'.
  • Intrinsic means the language won't allow you to use itself to cause UB.
  • Making an airplane crash or deleting your database is not UB, therefore 'safe' from Rust's perspective.
  • Writing to /proc/[pid]/mem to self-modify your code is also 'safe', resulting UB not caused intrinsincally.
let y = x + x;  // Safe Rust only guarantees the execution of this code is consistent with
print(y);       // 'specification' (long story …). It does not guarantee that y is 2x
                // (X::add might be implemented badly) nor that y is printed (Y::fmt may panic).

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 (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 maybe_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

  • 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<T>(x: &T) -> &u128 {      // Signature looks safe to users. Happens to be
    unsafe { mem::transmute(x) }         // ok if invoked with an &u128, UB for practically
}                                        // everything else.
 

Responsible use of Unsafe 💬

  • Do not use unsafe unless you absolutely have to.
  • Follow the Rust 死灵书, Unsafe Guidelines, always follow all safety rules, 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.
 

Adversarial Code 🧠url 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Adversarial code is safe 3rd party code that compiles but does not follow API expectations, and might interfere with your own (safety) guarantees.

You authorUser code may possibly …
fn g<F: Fn()>(f: F) {}Unexpectedly panic.
struct S<X: T> {}Implement T badly, e.g., misuse Deref, …
macro_rules! m {}Do all of the above; call site can have weird scope.
 
Risk PatternDescription
#[repr(packed)]Packed alignment can make reference &s.x invalid.
impl std::for S {}Any trait impl, esp. std::ops may be broken. In particular …
     impl Deref for S {}May randomly Deref, e.g., s.x != s.x, or panic.
     impl PartialEq for S {}May violate equality rules; panic.
     impl Eq for S {}May cause s != s; panic; must not use s in HashMap & co.
     impl Hash for S {}May violate hashing rules; panic; must not use s in HashMap & co.
     impl Ord for S {}May violate ordering rules; panic; must not use s in BTreeMap & co.
     impl Index for S {}May randomly index, e.g., s[x] != s[x]; panic.
     impl Drop for S {}May run code or panic end of scope {}, during assignment s = new_s.
panic!()User code can panic any time, resulting in abort or unwind.
catch_unwind(|| s.f(panicky))Also, caller might force observation of broken state in s.
let= f();Variable name can affect order of Drop execution. 1 🛑

1 Notably, when you rename a variable from _x to _ you will also change Drop behavior since you change semantics. A variable named _x will have Drop::drop() executed at the end of its scope, a variable named _ can have it executed immediately on 'apparent' assignment ('apparent' because a binding named _ means wildcard REF discard this, which will happen as soon as feasible, often right away)!

 

Implications

  • Generic code cannot be safe if safety depends on type cooperation w.r.t. most (std::) traits.
  • If type cooperation is needed you must use unsafe traits (prob. implement your own).
  • You must consider random code execution at unexpected places (e.g., re-assignments, scope end).
  • You may still be observable after a worst-case panic.

As a corollary, safe-but-deadly code (e.g., airplane_speed<T>()) should probably also follow these guides.

 

API Stabilityurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

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.
🔴 Removing Cargo features.
🟡 Altering existing Cargo 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.
🔴 Implementing any "fundamental" trait, as not implementing a fundamental trait already was a promise.
🟡 Adding a defaulted item; might cause dispatch ambiguity with other existing trait.
🟡 Adding a defaulted type parameter.
🟡 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., <T> to <T: Clone>).
🟡 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.
 

Miscurl

Specialty books, also see Little Book of Rust Books.

Topic ️📚Description
API GuidelinesHow to write idiomatic and re-usable Rust.
Asynchronous Programming 🚧Explains async code, Futures, …
CargoHow to use cargo and write Cargo.toml.
CLIsInformation about creating CLI tools.
CookbookCollection of simple examples that demonstrate good practices.
Design PatternsIdioms, Patterns, Anti-Patterns.
Edition GuideWorking with Rust 2015, Rust 2018, and beyond.
EmbeddedWorking with embedded and #![no_std] devices.
Functional Jargon 🧠A collection of functional programming jargon explained in Rust.
Guide to Rustc Development 🧠Explains how the compiler works internally.
Little Book of Rust MacrosCommunity's collective knowledge of Rust macros.
PerformanceTechniques to improve the speed and memory usage.
RFCs 🧠Look up accepted RFCs and how they change the language.
RustdocTips how to customize cargo doc and rustdoc.
Unsafe Code Guidelines 🚧Concise information about writing unsafe code.
Unstable 🧠Information about unstable items, e.g, #![feature(…)].
 

Comprehensive lookup tables for common components.

Table 📋Description
Rust ForgeLists release train and links for people working on the compiler.
     Supported PlatformsAll supported platforms and their Tier.
     Component History 🚧Check nightly status of various Rust tools for a platform.
Clippy LintsAll the clippy lints you might be interested in.
Rustfmt ConfigAll rustfmt options you can use in .rustfmt.toml.
 

Online services which provide information or tooling.

Service ⚙️Description
Rust PlaygroundTry and share snippets of Rust code.
crates.ioAll 3rd party libraries for Rust.
lib.rsUnofficial overview of quality Rust libraries and applications.
blessed.rs 💬An unofficial guide to the Rust ecosystem, even more opinionated.
std.rsShortcut to std documentation.
stdrs.dev 🧠Shortcut to std documentation including compiler-internal modules.
docs.rsDocumentation for 3rd party libraries, automatically generated from source.
releases.rsRelease notes for previous and upcoming versions.
query.rsA search engine for Rust.
 

Printing & PDFurl 💗😊 💩😠 ✏️ See privacy policy; also CTRL-ENTER submits. Submit

Want this Rust cheat sheet as a PDF? Generate it yourself via File > Print and then "Save as PDF" (works great in Chrome, has some issues in Firefox).