| name | rust-linear-type |
| description | 线性类型与资源管理专家。处理 Destructible, 资源清理, RAII, unique object, linear semantics, 线性语义, 资源所有权, 独占语义--- |
线性类型 vs Rust 所有权
| 特性 | Rust 所有权 | 线性类型 |
|---|
| 移动语义 | ✓ | ✓ |
| 复制语义 | 可选 | ✗ |
| 析构保证 | Drop | Destructible |
| 借用 | ✓ | ✗ 或受限 |
| 多重所有 | Rc/Arc | ✗ |
Rust 默认不是线性类型,但可以通过模式实现线性语义。
Destructible Trait
use std::mem::ManuallyDrop;
struct LinearBuffer {
ptr: *mut u8,
size: usize,
}
impl Drop for LinearBuffer {
fn drop(&mut self) {
unsafe {
std::alloc::dealloc(self.ptr, Layout::array::<u8>(self.size).unwrap());
}
}
}
struct SafeLinearBuffer {
inner: ManuallyDrop<LinearBuffer>,
}
impl Drop for SafeLinearBuffer {
fn drop(&mut self) {
unsafe {
ManuallyDrop::drop(&mut self.inner);
}
}
}
独占对象模式
#[derive(Copy, Clone)]
struct FileHandle(u32);
impl FileHandle {
fn from_raw(fd: u32) -> Self {
Self(fd)
}
}
struct LinearFile {
fd: FileHandle,
}
impl LinearFile {
pub fn open(path: &str) -> Result<Self, std::io::Error> {
Ok(LinearFile {
fd: FileHandle::from_raw(0),
})
}
pub fn consume(self) -> FileHandle {
self.fd
}
}
资源令牌模式
struct ResourceToken<T> {
resource: T,
consumed: bool,
}
impl<T> ResourceToken<T> {
pub fn new(resource: T) -> Self {
Self {
resource,
consumed: false,
}
}
pub fn consume(mut self) -> T {
self.consumed = true;
self.resource
}
pub fn is_consumed(&self) -> bool {
self.consumed
}
}
fn process_resource(token: ResourceToken<Vec<u8>>) -> Vec<u8> {
let data = token.consume();
data
}
交易式资源管理
struct Transaction<T> {
data: T,
committed: bool,
}
impl<T> Transaction<T> {
pub fn new(data: T) -> Self {
Self {
data,
committed: false,
}
}
pub fn commit(mut self) -> T {
self.committed = true;
self.data
}
pub fn rollback(self) {
}
}
fn example() -> Result<i32, ()> {
let tx = Transaction::new(100);
if condition {
tx.commit();
} else {
tx.rollback();
}
}
Unique 指针模式
struct UniquePtr<T: Sized> {
ptr: *mut T,
_marker: std::marker::PhantomData<T>,
}
impl<T> UniquePtr<T> {
pub fn new(data: T) -> Self {
let ptr = Box::into_raw(Box::new(data));
Self {
ptr,
_marker: std::marker::PhantomData,
}
}
pub fn as_ref(&self) -> Option<&T> {
if self.ptr.is_null() {
None
} else {
Some(unsafe { &*self.ptr })
}
}
pub fn into_box(self) -> Box<T> {
unsafe {
let ptr = self.ptr;
std::mem::forget(self);
Box::from_raw(ptr)
}
}
}
impl<T> Drop for UniquePtr<T> {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe {
Box::from_raw(self.ptr);
}
}
}
}
Rust 中的线性语义场景
| 场景 | 线性保证 | 模式 |
|---|
| 文件句柄 | close 恰好一次 | RAII + Drop |
| 网络连接 | close 恰好一次 | RAII + Drop |
| 内存分配 | free 恰好一次 | RAII + Drop |
| 锁 | unlock 恰好一次 | RAII + Drop |
| 事务 | commit 或 rollback | 交易式资源管理 |
| FFI 资源 | release 恰好一次 | 资源令牌 |
避免的模式
| 反模式 | 问题 | 正确做法 |
|---|
| Clone 允许复制 | 破坏线性语义 | 使用 move 语义 |
| Rc/Arc 共享 | 多重所有 | 线性令牌 |
| 手动管理生命周期 | 容易出错 | RAII + Drop |
| 跳过 Drop | 资源泄漏 | 使用 scope API |
与其他技能关联
rust-linear-type
│
├─► rust-resource → RAII 和 Drop 实现
├─► rust-ownership → 所有权模式
└─► rust-unsafe → 底层资源操作