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 是动态大小的类型,必须用引用或其它指针获取