Table of contents
Summary
针对概念性的,如果不看就会忘掉的东西 并且对相对比较复杂的地方需要做一下自己的理解 不要把精力放在过于花哨的类型系统和语法糖,只需要知道有这个东西就行 这里根据我看到的东西查缺补漏,之前一直没有做系统化的整理
循环控制结构
for/while/loop break 可以通过label 标记作用域跳出去
match
https://doc.rust-lang.org/stable/reference/expressions/match-expr.html
生命周期
更小的生命周期可以被比他大的生命周期覆盖 ‘a 能被 ‘static替代 衍生话题就是hrtb,实际上rust的subtyping只有在生命周期有这个概念 即需求’a生命周期的能用比他更大的’b来实现 这个貌似内部是通过hrtb for<‘a>这样去在调用者生成’a,延长到跟更长的生命周期里面去,或者说调用者的生命周期(这个理解需要后续确认) 生命周期的逆变 协变 https://zhuanlan.zhihu.com/p/682880532 逆变的话就可以变成fn(&str)->impl ToString 参数可以变成impl ToString, implToString->string
A <: B代表A是B的子类型A -> B以A为参数类型, 以B为返回值类型的函数类型x : Ax是一个变量, 其类型为A
Drop Check
类似C++的RAII 为了使泛型类型能够正确实现 drop,其泛型参数必须严格地比其存活时间长。 **For a generic type to soundly implement drop, its generics arguments must strictly outlive it. drop order 顺序
dyn Trait
虚表结构 多重继承里面就必须在保存一个指向虚表的指针 从实践来说的话就是如果使用组合继承的dyn Trait 是有一定性能上的影响的 https://lancern.xyz/posts/2024/01/rust-vtable
+-------------------------------+
| fn drop_in_place(*mut T) |
+-------------------------------+
| size of T |
+-------------------------------+
| align of T |
+-------------------------------+
| fn <T as Grand>::grand_fun1 |
+-------------------------------+
| fn <T as Grand>::grand_fun2 |
+-------------------------------+
| fn <T as Parent>::parent_fun1 |
+-------------------------------+
| fn <T as Parent>::parent_fun2 |
+-------------------------------+
| fn <T as Trait>::fun |
+-------------------------------+Specialization
这个理解成对Trait 的方法实现默认的不完全方法 可以通过泛型参数T的约束或者具体的类型来覆盖 目前可以通过mini specialization 开启
Union
可能会用到?
Atomic相关
基本是C++的心智模型
try-trait v2
没仔细看,但是这个貌似把result,option 等等全部做了一个抽象
1pub trait Try: FromResidual {
2 type Output;
3 type Residual;
4
5 // Required methods
6 fn from_output(output: Self::Output) -> Self;
7 fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
8}ManuallyDrop
延迟Drop,对应的还有修改drop的行为
PhantomData
伪造生命周期或者各种东西 也可以用来模拟持有某一个数据的所有权
Future Pin trait &Pin Project
Future表现为一个通过不断调用poll Pending直到Ready(T)的一个抽象定义
MaybeUninit
延迟初始化,也就是我可以一部分初始化 比如提前声明足够大的CAP
Unsafe
如果用上unsafe跟C++没差了 如果你能证明生命周期上没问题的话你可以自己绕过去
temporary-lifetimes-in-tail-expressions
https://rust-lang.github.io/rfcs/3606-temporary-lifetimes-in-tail-expressions.html 作为解决方法之一是super let ,rust目前在不同的语句里面的释放周期并没有一致的规则 可能会导致死锁
- super let 形如f(g(&T)) 以及在if 和if let 语句里面最内层引用会被自动的生命周期延长 这块实践过程中间一般都是通过直接拆成单句来解决 在结构体中间放置指针会阻止他释放直到结束 这里super let 用来把pin!改成普通函数,format_arg 但是这个目前有一定争议
NicheOptimization
rust 会通过各种方式记录访问的值域,如果用不到的值域就会被用去做这个优化(enum) https://www.0xatticus.com/posts/understanding_rust_niche/
precise-capturing
用于指定哪些泛型参数应在类似 RPIT 的 impl Trait 类型中捕获
比如目前不允许嵌套透明对象中间捕获更高的生命周期可以通过use<> 来防止捕获进而通过编译
https://rust-lang.github.io/rfcs/3617-precise-capturing.html
可变参数泛型
https://github.com/rust-lang/lang-team/blob/master/src/design_notes/variadic_generics.md frunk 可以设计一个tuple来实现一个非同质的列表,并且可以编译期去指定他的类型名称(利用自动的函数推导) 目前我理解的rust实现的方案是 Tuple<T1,T2….Tn>=>Tupe<T1,Tuple<T2,….>> 这个也可以在axum和bevy中看到 对于想实现impl Mytrait<(T1)> for T …直到T1,T2,T3….Tn只能使用宏来实现 这个在axum的handler模型里面有见到过 这个也是属于万恶之源的一部分,只能使用形如这种形式来做到 e.g. axum
1macro_rules! all_the_tuples {
2 ($name:ident) => {
3 $name!([], T1);
4 $name!([T1], T2);
5 $name!([T1, T2], T3);
6 $name!([T1, T2, T3], T4);
7 $name!([T1, T2, T3, T4], T5);
8 $name!([T1, T2, T3, T4, T5], T6);
9 $name!([T1, T2, T3, T4, T5, T6], T7);
10 $name!([T1, T2, T3, T4, T5, T6, T7], T8);
11 $name!([T1, T2, T3, T4, T5, T6, T7, T8], T9);
12 $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9], T10);
13 $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T11);
14 $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], T12);
15 $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], T13);
16 $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], T14);
17 $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], T15);
18 $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], T16);
19 };
20}ZST/DST
trait object & str,[u8] ZST=> () 还有enum sth{}
Substructural Types
https://faultlore.com/blah/linear-rust/ 需要再读一读
- can be used any number of times (no name - the default) 可以使用任意次数(无名称 - 默认) Copy 和Clone
- can’t be used more than once (affine™) 不能使用超过一次(affine™) <=1 当我移动了就不能用之前的变量,以及变量遮蔽
- must be used at least once (relevant™ - this one is a decent name) >=1 必须至少使用一次(relevant™ - 这是一个不错的名字) drop 和lint就是这个性质 dorp可以通过mem::forget这样去移除行为
- must be used exactly once (linear™) =1 必须使用一次(线性™)
Autodiff
自动计算微分 比如说
1fn f(x: f32) -> f32{
2 x * x
3}可以自动计算这个
1fn df(x: f32) -> (f32, f32) {
2 (x*x, 2.0 * x)
3}https://www.reddit.com/r/rust/comments/1h1b1po/using_stdautodiff_to_replace_jax/ 在Python/JAX中一般使用的时候使用的是jit编译时间可能需要数小时甚至数天 Rust 的 autodiff 可以在大约 30 分钟内编译出等效的 Rust 代码 具体工作层级是在 Enzyme https://enzyme.mit.edu/ llvm ir
内部可变性
Rc Weak UniqueRc Arc
RefCell & Cell & UnsafeCell
由于 Rust 的 mutable 特性,一个结构体中的字段,要么全都是 immutable,要么全部是 mutable,不支持针对部分字段进行设置。比如,在一个 struct 中,可能只有个别的字段需要修改,而其他字段并不需要修改,为了一个字段而将整个 struct 变为 &mut 也是不合理的。
所以,实现 内部可变性 的 Cell 和 RefCell 正是为了解决诸如这类问题存在的,通过它们可以实现 struct 部分字段可变,而不用将整个 struct 设置为 mutable。
- 部分借用这个问题其实在游戏领域非常常见 所以有view type 出现,因为大多数情况我们都是同一个结构体部分借用 https://smallcultfollowing.com/babysteps/blog/2021/11/05/view-types/ https://hackmd.io/J5aGp1ptT46lqLmPVVOxzg?view 最近讨论到了V3阶段
异步
主要是以tokio为主的异步库 然后和thread-per-core 类型的特殊异步库 以及使用了Io_uring的特殊库=>monoio linux 包括epoll,poll,
trait 泛型
Send/Sync
Send 是 我可以将其移动到另一个线程而不创建非线程安全的隐藏共享状态吗? Rc 移动后保持共享状态
Sync 是 多个线程是否可以保存对此的引用,而不会发生突变竞争(当且仅当&T是Send的时候T才是Sync)
特例:
e.g. 通过通过libloading载入dll,为了防止线程局部变量出问题!Send(需要由创建他的线程释放),!Sync(并没有做读写访问控制)
e.g. MutexGuard<T> 由于pthread平台限制(如果你把它发送到别的线程那么析构函数会在被发送到的线程执行,但是你可以Send &MutexGuard<T>) !Send Sync(因为要Deref)
https://blog.cuongle.dev/p/this-sendsync-secret-separates-professional-and-amateur
FFI
宏
过程宏
能不用quote还是别用 因为这个会极大的印象展开时间
tt咀嚼机
奇怪语法糖
泛型相关
HRTB
具体表现是for<‘a>:Trait<‘a> 将生命周期推迟到调用者来决定 最经典是serde里面的使用
1//将生命周期推迟到调用者来决定
2serde DeserializeOwned
3
4fn with_nested_refcell<'nested> (
5 nested: &'nested RefCell<RefCell<str>>,
6 f: for<'arg> fn(&'arg str), // ≈ fn<'arg> (&'arg str),
7)
8{
9 f(&*nested.borrow().borrow())
10}RPIT
return position impl Trait RPIT 并不是泛型
1fn test_rpit() -> impl Iterator<Item = i32> {
2 [1, 2, 3, 4].into_iter()
3 }GAT
泛型关联类型
1trait ConvertTo: Sized {
2 type Inner;
3 type Output<Item>
4 where
5 Item: From<Self::Inner>;
6 fn convert_to<U>(self) -> Self::Output<U>
7 where
8 U: From<Self::Inner>;
9}我可以将函数上面的泛型类型参数U和Output泛型类型参数绑定到一起(生命周期什么的都可以传递过去了)
RPITIT
Return Position impl Trait In Traits
RPITIT 解糖为关联类型或 GAT
AFIT
rust的async函数脱糖目标是RPIT(也是#[async_trait] expend的结果)
1async fn get_one()->i32{
2 1
3}
4//脱糖到
5fn get_one() -> impl Future<Output=i32>{
6 async {1}
7}同样道理
async fn In Traits
就会脱糖到RPITIT
AFIT-> RPITIT -> GAT/关联类型
RTN
https://rust-lang.github.io/rfcs/3654-return-type-notation.html#where-rtn-can-be-used-for-now 一个是where子句上面的
1trait Foo {
2 fn bar() -> impl Sized;
3}
4
5fn is_send(_: impl Send) {}
6
7fn test<T>()
8where
9 T: Foo,
10 T::bar(..): Send,
11{
12 is_send(T::bar());
13}在关联类型上面
1#![feature(return_type_notation)]
2
3trait Foo {
4 fn bar() -> impl Sized;
5}
6
7fn is_send(_: impl Send) {}
8
9fn test<T>()
10where
11 T: Foo<bar(..): Send>,
12{
13 is_send(T::bar());
14}