Rustling总结 我想成为Rustacean,通过rustling的练习,我学到一些没接触过的Rust知识
repr attribute 在c/c++中,常常使用attribute ((align))的方式来确保内存对齐,repr在rust的功能久石让每一个数据能够按照k的整数倍分配,k通常是基本类型。
Rusty 阶乘 1 2 3 pub fn factorial (num: u64 ) -> u64 { (1 ..=num).fold(1 , |acc, x| acc * x) }
范围 (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 选择退出 &T 的不可变性保证:共享的引用 &UnsafeCell 可能指向正在发生可变的数据。这称为内部可变性。
所有其他允许内部可变性的类型,例如 Cell 和 RefCell,在内部使用 UnsafeCell 来包装它们的数据。
所有其他允许内部可变性的类型,例如 Cell 和 RefCell,在内部使用 UnsafeCell 来包装它们的数据。
UnsafeCell API 本身在技术上非常简单: .get() 为其内容提供了裸指针 *mut T。正确使用该裸指针取决于您。
如果您使用生命周期 ‘a (&T 或 &mut T 引用) 创建安全引用,那么您不得以任何与 ‘a 其余部分的引用相矛盾的方式访问数据。 例如,这意味着如果您从 UnsafeCell 中取出 *mut T 并将其转换为 &T,则 T 中的数据必须保持不可变 (当然,对 T 中找到的任何 UnsafeCell 数据取模),直到引用的生命周期到期为止。 同样,如果您创建的 &mut T 引用已发布为安全代码,则在引用终止之前,您不得访问 UnsafeCell 中的数据。
对于没有 UnsafeCell<> 的 &T 和 &mut T,在引用过期之前,您也不得释放数据。作为一个特殊的例外,给定一个 &T,它在 UnsafeCell< > 内的任何部分都可能在引用的生命周期期间被释放,在最后一次使用引用之后 (解引用或重新借用)。 因为您不能释放引用指向的部分,这意味着只有当它的每一部分 (包括填充) 都在 UnsafeCell 中时,&T 指向的内存才能被释放。
但是,无论何时构造或解引用 &UnsafeCell,它仍必须指向活动内存,并且如果编译器可以证明该内存尚未被释放,则允许编译器插入虚假读取。
在任何时候,您都必须避免数据竞争。如果多个线程可以访问同一个 UnsafeCell,那么任何写操作都必须在与所有其他访问 (或使用原子) 相关之前发生正确的事件。
为了帮助进行正确的设计,以下情况明确声明为单线程代码合法:
&T 引用可以释放为安全代码,并且可以与其他 &T 引用共存,但不能与 &mut T 共存
&mut T 引用可以发布为安全代码,前提是其他 &mut T 和 &T 都不共存。&mut T 必须始终是唯一的。
请注意,虽然可以更改 &UnsafeCell 的内容 (即使其他 &UnsafeCell 引用了该 cell 的别名) 也可以 (只要以其他方式实现上述不变量即可),但是具有多个 &mut UnsafeCell 别名仍然是未定义的行为。 也就是说,UnsafeCell 是一个包装器,旨在通过 &UnsafeCell<> 与 shared accesses (i.e. 进行特殊交互 (引用) ; 通过 &mut UnsafeCell< > 处理 exclusive accesses (e.g. 时没有任何魔术) : 在该 &mut 借用期间, cell 和包装值都不能被别名。
.get_mut() 访问器展示了这一点,该访问器是产生 &mut T 的 safe getter。
UnsafeCell 与其内部类型 T 具有相同的内存表示。此保证的结果是可以在 T 和 UnsafeCell 之间进行转换。 将 Outer 类型内的嵌套 T 转换为 Outer<UnsafeCell> 类型时必须特别小心: 当 Outer 类型启用 niche 优化时,这不是正确的。
std::mem::MaybeUninit Rust 编译器要求变量要根据其类型正确初始化。
比如引用类型的变量必须对齐且非空。这是一个必须始终坚持的不变量,即使在 Unsafe 代码中也是如此。因此,零初始化引用类型的变量会导致立即未定义行为,无论该引用是否访问过内存。
编译器利用这一点,进行各种优化,并且可以省略运行时检查。
由调用者来保证MaybeUninit确实处于初始化状态。当内存尚未完全初始化时调用 assume_init() 会导致立即未定义的行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #![allow(unused)] fn main () {use std::mem::{self , MaybeUninit};let x: &i32 = unsafe { mem::zeroed() }; let x: &i32 = unsafe { MaybeUninit::zeroed().assume_init() }; let b: bool = unsafe { mem::uninitialized() }; let b: bool = unsafe { MaybeUninit::uninit().assume_init() }; let x: i32 = unsafe { mem::uninitialized() }; let x: i32 = unsafe { MaybeUninit::uninit().assume_init() }; let mut vec: Vec <u8 > = Vec ::with_capacity(1000 );unsafe { vec.set_len(1000 ); }reader.read(&mut vec); }
主要就是这些收获。