Rust 内存布局
借用规则
- 任意时刻,只能拥有一个可变引用或任意个不可变引用(可变与不可变不能同时存在)
- 引用必须总是有效
借用类型
borrow() 返回 Ref<T> 类型的智能指针;
borrow_mut() 返回 RefMut<T> 类型的智能指针;
两者都实现了 Deref 可作为常规引用。
线程安全类型
Send 允许在线程间传递所有权,Sync 允许多线程访问
如果 &T 是 Send 的话,T 就是 Sync,其引用可以安全地发送到另一个线程
智能指针
Box
使用栈空间需要在编译时知道数据大小,Box 提供在栈空间固定的大小(1 个机器字),指向在堆分配的空间
使用 Pin 固定
Pin<P>阻止所包装的指针指向的数据被移动Pin与Unpin只影响被指向的数据P::Target
构建一个 Pin<Box<T>> ,如果 T 未实现 Unpin ,那么 x 会被固定在内存中无法移动
如果 Self 满足 Unpin ,从 Pin<&mut Self> 会隐式转换到 &mut Self
在成员不满足 Unpin 的情况下,从 Pin<&mut Self> 转换到 &mut Self
// 获取不满足 `Unpin` 的成员
let (mut sleep, reader) = unsafe {
let this = self.get_unchecked_mut();
(
Pin::new_unchecked(&mut this.sleep),
Pin::new_unchecked(&mut this.reader),
)
}
// 使用 `pin_project`
#[pin_project]
struct SlowRead<R> {
#[pin]
reader: R,
#[pin]
sleep: Sleep,
}
// 调用 `project` 获取自动生成的结果
let mut this = self.project();
在 receiver 不满足 Unpin 的情况下,创建 Pin,需要确保 pin 之后不再使用 unpin
let mut f = SlowRead::new();
// 创建自身的 `pin`,并 shadow 自身的 `unpin`
let mut f = unsafe { Pin::new_unchecked(&mut f) };
f.read_exact(&mut buf).await?;
// 使用 `pin_utils`
pin_utils::pin_mut!(f);
Rc
允许对同一数据创造多个只读引用,并在最后一个引用离开作用域时释放数据的内存。
Rc 未实现 Send 和 Sync,因为多个线程同时访问或修改引用计数会导致数据竞争;可使用 Arc 替代
Cell
RefCell
在运行时检查借用规则,提供内部可变性
impl Messenger for MockMessenger {
fn send(&self, messenger: &str) {
self.sent_messenges.borrow_mut().push(String::from(messenger));
}
}
Trait object
- trait object 是储存数据指针 (data pointer) 与虚表指针 (vtable pointer) 的胖指针 (fat pointer);
- data pointer 指向原数据;
- vtable pointer 指向实现某种 trait 的 vtable,vtable 在编译时生成,共享给相同类型的所有 object;
- 在进行类型转换时,通过在原指针后添加相应 vtable pointer 形成胖指针。
trait object 是动态大小的类型,必须用引用或其它指针获取
闭包类型
闭包会通过与外部数据的交互实现不同的 trait
FnOnce
闭包获取了外部数据的所有权,且只可调用一次。数据在调用过程中被移动或释放。
struct MyClosure {
name: String
}
impl FnOnce for MyClosure {
fn call_once(self) {
drop(self.name)
}
}
FnMut
闭包获取了外部数据的可变引用。
闭包需要是 mut,因为需要获取自身的可变引用。
struct MyClosure {
i: &mut i32,
}
impl FnMut for MyClosure {
fn call_mut(&mut self) {
*self.i += 1;
}
}
Fn
闭包获取了外部数据的只读引用。
struct MyClosure {
i: &String,
}
impl Fn for MyClosure {
fn call(&self) {
println!("{}", self.msg);
}
}
move 关键字
move - Rust > Closures: Anonymous Functions that Capture Their Environment
在闭包内,将通过引用或可变引用获取的变量转化为由值获取的变量。
如果闭包内使用了数据的引用,但闭包与引用数据的生命周期不同,使用 move 来明确获取数据所有权。