Rust 笔记
一句话笔记
- 用 rustup 脚本进行 Windows 平台 MSVC ABI 安装时,可以选择仅下载安装构建工具 Microsoft C++ Build Tools 且 仅安装所需组件
const
是内联常量;而static
是静态变量,在内存中独一份- 表达式优先级有
::
>.
> 数组访问 >?
>*
&
&mut
>as
> 四则运算 > …… ?
返回时会调用Error
的into()
方法以匹配返回类型- 共享引用
&T
实现了Copy
,而可变引用&mut T
没有 T
作为泛型标注不仅包含 所有权类型 也包含 共享引用 和 可变引用 ,但这两种引用类型互不包含。 来源
-
mod
关键字用于将其后的模块导入,既可以行内、也可以通过同名文件或同名文件夹下的mod.rs
;use
关键字创建 已导入 的模块内容的“快捷方式”。来源 -
fn
与Fn
,前者是函数指针、后者是一个 Trait;这意味着前者为编译时的函数,后者多为闭包。 -
对于形如
ident<T>
的泛型声明(ident
可为函数、方法、结构体、枚举等等),我们往往可以ident::<T>
来指定泛型参数,此语法被称作“涡轮鱼”。 来源 -
Borrow
与AsRef
两 trait 的区别在于:前者表示二者本质相同(哈希一致,比如String
与&str
),后者不然(String
与[u8]
)。 来源 -
Clone
是Copy
的前置 trait,前者允许被复制,后者使复制时常发生 来源 -
标注在函数与 impl 上的生命周期,都表示这个生命周期来自函数作用域外,说明带有这个生命周期的引用、即便超出函数作用域还能存活;而来自于函数内的引用往往在函数退出时便失效了。来源
-
使用
1
RUSTFLAGS='-C target-feature=+crt-static' cargo b -r --target x86_64-unknown-linux-gnu
静态编译Linux下的二进制文件。 来源
-
Rust 可以应用设计模式,见 Rust Design Patterns
-
Rust 的迭代器失效问题(iterator invalidation)经常会被编译器指出
长一些的知识点
杂项
-
又称 trait object;由于一种 Trait 对不同类型的实现(
impl
)可能不同,我们便可以使用dyn
来表示这些类型,例如Box<dyn Debug>
(不同实现大小不同,故需要放在堆上)dyn Trait
在内存中表现为一个胖指针,其 data 段存储一些数据(例如 虚表 的容量、长度等等),vtable 则是指向 虚表(别名 虚函数表、vtable、虚表、vftable……)的指针;虚表 存储了不同类型的实现对比 泛型:
- 泛型 将所有实现的可能性枚举出来并编译,故称为 静态分发
- trait object 则将实现储存在内存中按需取用,故称为 动态分发
大多数情况下,泛型 使运行速度更快、编译更慢,trait object 则相反、且写起来更“精简”
但是 trait object 要求 Trait 对象安全
-
- 作为生命周期(例如
let s: &'static str = ……
、static s: u8 = ……
)时,表示s
活得比'static
长,即在整个程序运行期间都将存在 - 作为 trait 约束(例如
…… where T: 'static ……
、…… t: impl 'static ……
) 时,表示T
不接受活得比'static
短的引用,即既接受生命周期至少为static
的引用,又接受非引用(所有权)类型
以上内容可以拓展至任意
'a
来源 - 作为生命周期(例如
-
位置表达式与值表达式(左值与右值):
- 位置表达式,表示内存位置(该位置一般存了值)。仅表现为:局部变量、静态变量、
*expr
、expr[...]
、expr.f
几种表达式 - 值表达式,表示值。非位置表达式皆为值表达式
通常地,我们称代码中书写表达式的地方为上下文;结合两种表达式的来历,就可以得知存在两种上下文:
- 当一个 上下文 需要内存中的位置,它便是 位置表达式上下文。
仅表现为:赋值左侧、借用(含隐式借用)、被索引、被访问字段、let ... =
右侧、
if/while let ... =
或match
右侧、
..
右侧
的操作数 - 当一个 上下文 需要值,它便是 值表达式上下文。非位置表达式上下文皆为值表达式上下文
那么自然地,需要研究表达式与上下文的结合:
- 值表达式 在 值表达式上下文 中,没问题
- 位置表达式 在 位置表达式上下文 中,也没问题
- 位置表达式 在 值表达式上下文 中,没什么大不了,将此位置的值拿来用就好了
- 值表达式 在 位置表达式上下文 中,看情况,有时坚决不行、有时能用临时值凑合
- 位置表达式,表示内存位置(该位置一般存了值)。仅表现为:局部变量、静态变量、
-
move 与 copy 的条件:当 位置表达式 在 值表达式上下文 中,若位置表达式实现了
Copy
,则其值会被 复制(copy
) 出来;若没有,但其实现了Sized
,则其值会被 移出(move
) 原位置表达式(移出后该位置将无法被访问),移出条件:- 位置表达式未被借用
- 位置表达式是临时值
- 位置表达式表现为对
Box<T>
的解引用,且里面的值可被移出 - 位置表达式被访问字段,且该字段未实现
Drop
且能被移出
-
析构:
隐式行为
-
调整 rust-analyzer 的 Inlay Hints 设置似乎可以间接查看一些隐式行为的发生
-
对于实现了
Deref<Target = U>
的T
与其实例t
,且U
类型有对应t
的实例u
,存在以下隐式行为:-
使得
*t
等效于_(&t).deref()
,得到u
_ (deref()
的定义:接收&self
并返回&Target
类型)(这里发生了查找方法调用*) -
允许 将
&t
强制转换为&u
rust-analyzer 显示的
&String
到&str
的过程:已知String
实现了Deref<Target = str>
,那么s: &String
可以通过*s
可直接解为String
,再*(*s)
deref 至str
,最终&(*(*s))
后得到&str
DerefMut
性质相似 -
-
这是一个隐式行为;此行为在
let const static
声明、结构体初始化、函数传参、函数返回 时若 实际值的类型与声明类型不匹配 就会触发——编译器将尝试对此值不断进行:- 可变转为不可变
- 对容器类型的内容进行类型强制转换
- ……
直到匹配声明类型;
此隐式行为亦可用
as
显式触发 -
这是一个隐式行为;访问结构体
struct
的字段(比如struct.field
)时,若结构体实现了Deref
或DerefMut
,则它将被尽可能多地解引用直到能够访问此字段 -
这是一个隐式行为;在调用结构体方法(这里以
Box<[i8; 8]>.zip()
为例)时,会先 被尽可能多地解引用 并在最后 尝试非固定尺寸类型自动强转 以构建一个列表 :Box<[i8; 8]>
(结构体本身)[i8; 8]
(解引用)[i8]
(非固定尺寸类型自动强转)
再按列表顺序添加每项的
T
、&T
、&mut T
:Box<[i8; 8]>
&Box<[i8; 8]>
&mut Box<[i8; 8]>
[i8; 8]
&[i8; 8]
&mut [i8; 8]
[i8]
&[i8]
&mut [i8]
最后按列表顺序查找 可见 的
zip
方法,且优先查找直接 impl 的方法、然后才查找 trait 中的方法 -
这是一个并未被详细说明的隐式行为;当且仅当
let
声明、函数形参等等 显式要求传入可变引用(形如_: &mut T
)时,编译器不会移动已有可变引用t: &mut T
(可变引用没有实现Copy
),而是通过&mut *t
创建新的可变引用并传入,此时原引用被“锁定”直到新引用失效编译器有时也会自动通过
&*t
创建新的共享引用 -
这是一个隐式行为,编译器会在某些情况下自动添加生命周期标识,所以一般仅在函数(不适用于闭包)有 多于一个传入引用且返回引用 时需要标注生命周期
Unsafe 相关
- 子类型:
由于 长生命周期'long
往往可以应用到需要 短生命周期'short
的地方,即 “长周期 是 短周期”,于是我们定义较长周期是较短周期的子类型,由此可得'static
是所有生命周期的子类型