2个不稳定版本
0.2.0 | 2021年10月14日 |
---|---|
0.1.1 | 2021年5月26日 |
0.1.0 |
|
927 在 Rust模式 中排名
每月下载量 4,784
用于 6 个crate(5 个直接使用)
46KB
668 行
pin_init
Rust中的安全固定初始化。
问题
Rust的 Pin
为C互操作和自引用结构体提供了足够的保证 -- 一旦固定,它们的地址就稳定,并且在内存区域可以释放之前,确保调用析构函数。
这里的问题是“一旦固定”。 Pin
预期类型可以在不固定的情况下创建,并且可以在任何操作之前稍后固定。这对于没有任何自引用的 Generator
来说是可以接受的,因为自引用只能在轮询生成器时创建。对于其他类型,例如 pthread_mutex_t
,预期从开始就进行固定。
为了演示目的,我们将使用这个类型 NeedPin
struct NeedPin {
// Must points to itself
address: *const NeedPin,
_pinned: PhantomPinned,
}
impl NeedPin {
fn verify(&self) {
assert!(ptr::eq(self, self.address), "invariant not held");
}
}
impl Drop for NeedPin {
fn drop(&mut self) {
/* Must be called */
}
}
可以分离创建和初始化
impl NeedPin {
unsafe fn uninit() -> Self {
Self {
address: ptr::null(),
_pinned: PhantomPinned,
}
}
unsafe fn init(self: Pin<&mut Self>) -> Result<(), Error> {
let this = unsafe { self.get_unchecked_mut() };
this.address = this;
}
}
但这需要不安全,并且非常难以使用。
最终目标是
- 安全性。我们应该能够在没有不安全的情况下创建和使用这种固定类型。(显然,固定类型本身仍然是不安全的实现)。
- 易用性。语法不应该与常规Rust差异太大。
- 可聚合性。包含多个固定类型的结构体可以安全地一起创建和初始化。
- 无隐式分配。在初始化期间不应需要分配。用户应该能够指定是在box中还是栈上初始化。
- 可失败性。不对初始化的成功性做出假设。
解决方案:pin_init
此库提供了类型 PinUninit
和 InitResult
作为安全固定初始化的原语。有关这些类型的详细信息可以在各自的文档中找到,但简而言之,与普通非固定类型的(可能失败的)构造函数不同,pin_init
期望你提供一个类型为 for<'a> FnOnce(PinUninit<'a, T>) -> InitResult<'a, T, Err>
的构造函数。
NeedPin::new
可以这样定义:
impl NeedPin {
pub fn new() -> impl Init<Self, Infallible> {
init_from_closure(|mut this: PinUninit<'_, Self>| -> InitResult<'_, Self, Infallible> {
let v = this.get_mut().as_mut_ptr();
unsafe { *ptr::addr_of_mut!((*v).address) = v };
Ok(unsafe { this.init_ok() })
})
}
}
借助 Rust 的亲和类型系统和借用检查器,InitResult
实质上是关于类型是否已初始化的证书。NeedPin
现在可以轻松初始化。
// In a box
let p: Pin<Box<NeedPin>> = pin_init::new_box(NeedPin::new()).unwrap();
// On the stack
init_stack!(p = NeedPin::new());
let p: Pin<&mut NeedPin> = p.unwrap();
对于结构体,如果在定义结构体时使用了 #[pin_init]
,则 init_pin!
可以创建与结构体表达式非常相似的实例。也支持嵌套结构。
#[pin_init]
struct ManyPin {
#[pin]
a: NeedPin,
b: usize,
}
#[pin_init]
struct TooManyPin {
#[pin]
a: NeedPin,
#[pin]
b: ManyPin,
}
let p = new_box(init_pin!(TooManyPin {
a: NeedPin::new(),
b: ManyPin {
a: NeedPin::new(),
b: 0,
}),
}));
此库还提供了 UniqueRc
和 UniqueArc
,灵感来自 servo_arc。它们可以在共享之前可变地初始化 Rc
和 Arc
。提供了 Rc::pin_with
和 Arc::pin_with
,它们内部创建 UniqueRc
和 UniqueArc
,使用给定的构造函数进行固定初始化,并将它们转换为可共享的形式。
此库允许安全地初始化固定数据结构。可以使用 pin-project
安全地访问这些结构体。你可以将 #[pin_init]
和 #[pin_project]
与你的结构体一起使用,它们甚至共享相同的 #[pin]
字段属性!
请参阅 示例 以了解一些非人工示例。
依赖关系
~1.5MB
~35K SLoC