Rustling总结
我想成为Rustacean,通过rustling的练习,我学到一些没接触过的Rust知识
repr attribute
在c/c++中,常常使用attribute((align))的方式来确保内存对齐,repr在rust的功能久石让每一个数据能够按照k的整数倍分配,k通常是基本类型。
Rusty 阶乘
1 | pub fn factorial(num: u64) -> u64 { |
范围 (1..=num):
这个表达式创建了一个从 1 到 num 的范围,包括 num 本身。..= 是一个闭区间语法,表示这个范围是包含起始值和结束值的。
fold 方法:
fold 是一个高阶函数,它用于将一个迭代器中的所有元素合并成一个单一的值。
它的第一个参数是初始值,在这里是 1。这个值会作为累加器的初始状态。
第二个参数是一个闭包(匿名函数),它接受两个参数:acc(累加器的当前值)和 x(迭代器中的当前值)。
在每一次迭代中,闭包会将 acc 和 x 相乘,并返回新的累加器值。
流敏感分析
依靠 Borrow Checker 确保内存安全,原理如下:
执行路径分析:Rust 编译器在编译过程中会为每个变量跟踪其借用状态或所有权。在程序的不同控制流(如条件判断、循环、函数调用等)中,编译器会检查在这些不同路径下变量的状态变化,并在所有路径中保持一致性。
数据流分析:Rust 编译器通过数据流分析来确定变量的生存期、是否被借用、以及是否存在竞争条件。这个分析是流敏感的,意味着它会根据程序的控制流更新变量状态。
作用域检查:编译器在分析时会检查变量是否在作用域内、是否已经被销毁或者转移所有权。通过流敏感分析,编译器能够精确确定哪些变量在某条执行路径中被有效引用或借用。
take
在 Rust 中,take() 方法通常用于在某些容器类型(如 Option、Result 等)中“取走”值,将原来的值替换为一个默认值(通常是 None 或 Err),同时返回原来的值。
take() 经常用于以下场景:
转移所有权:从一个可选值中取出所有权,并清空该值,避免复制或克隆。
链表或树结构:当你遍历或修改链表或树结构时,可以用 take() 来“取走”节点的引用并避免借用冲突。
zero-cost futures
async/await实现的future类型不会引入任何额外的运行时开销。
无运行时依赖:与其他编程语言(如 JavaScript 或 Python)不同,Rust 的 async/await 本身不依赖特定的运行时机制。Future 是惰性的,它本质上是一个状态机,只有在被轮询时才会前进。虽然需要某种形式的运行时(如 Tokio 或 async-std)来调度异步任务,但这些运行时并没有与 async/await 特性本身紧耦合。
Any Trait
Rust中的Any trait允许在运行时进行类型检查和类型转换。这个类型在处理动态类型时较为有用。
功能:
类型检查:通过is::
()方法,可以检查一个值是否是某种特定类型。 类型转换:使用downcast_ref::
()和downcast:: ()方法,可以将一个&dyn Any或者Box 类型的值转换回具体类型T。
注意:Any trait只能用于’static 生命周期的类型,这意味着他不能用于包含非静态引用的类型。
并且,Any会引入运行时开销,因为它依赖动态分派。
UnsafeCell
Rust 中内部可变性的核心原语。
如果您使用的是 &T,则通常在 Rust 中,编译器基于 &T 指向不可变数据的知识来执行优化。例如通过别名或通过将 &T 转换为 &mut T 来可变的该数据,被认为是未定义的行为。 UnsafeCell
所有其他允许内部可变性的类型,例如 Cell
所有其他允许内部可变性的类型,例如 Cell
UnsafeCell API 本身在技术上非常简单: .get() 为其内容提供了裸指针 *mut T。正确使用该裸指针取决于您。
如果您使用生命周期 ‘a (&T 或 &mut T 引用) 创建安全引用,那么您不得以任何与 ‘a 其余部分的引用相矛盾的方式访问数据。 例如,这意味着如果您从 UnsafeCell
对于没有 UnsafeCell<> 的 &T 和 &mut T,在引用过期之前,您也不得释放数据。作为一个特殊的例外,给定一个 &T,它在 UnsafeCell<> 内的任何部分都可能在引用的生命周期期间被释放,在最后一次使用引用之后 (解引用或重新借用)。 因为您不能释放引用指向的部分,这意味着只有当它的每一部分 (包括填充) 都在 UnsafeCell 中时,&T 指向的内存才能被释放。
但是,无论何时构造或解引用 &UnsafeCell
在任何时候,您都必须避免数据竞争。如果多个线程可以访问同一个 UnsafeCell,那么任何写操作都必须在与所有其他访问 (或使用原子) 相关之前发生正确的事件。
为了帮助进行正确的设计,以下情况明确声明为单线程代码合法:
&T 引用可以释放为安全代码,并且可以与其他 &T 引用共存,但不能与 &mut T 共存
&mut T 引用可以发布为安全代码,前提是其他 &mut T 和 &T 都不共存。&mut T 必须始终是唯一的。
请注意,虽然可以更改 &UnsafeCell
.get_mut() 访问器展示了这一点,该访问器是产生 &mut T 的 safe getter。
UnsafeCell
std::mem::MaybeUninit
Rust 编译器要求变量要根据其类型正确初始化。
比如引用类型的变量必须对齐且非空。这是一个必须始终坚持的不变量,即使在 Unsafe 代码中也是如此。因此,零初始化引用类型的变量会导致立即未定义行为,无论该引用是否访问过内存。
编译器利用这一点,进行各种优化,并且可以省略运行时检查。
由调用者来保证MaybeUninit
1 |
|
主要就是这些收获。