#pin #attributes #macro

no-std pin-init

Rust中的安全固定初始化

2个不稳定版本

0.2.0 2021年10月14日
0.1.1 2021年5月26日
0.1.0 2021年5月25日

927Rust模式 中排名

Download history 241/week @ 2024-03-13 613/week @ 2024-03-20 632/week @ 2024-03-27 1532/week @ 2024-04-03 1590/week @ 2024-04-10 1439/week @ 2024-04-17 1109/week @ 2024-04-24 13/week @ 2024-05-01 139/week @ 2024-05-08 889/week @ 2024-05-15 259/week @ 2024-05-22 321/week @ 2024-05-29 971/week @ 2024-06-05 2237/week @ 2024-06-12 936/week @ 2024-06-19 493/week @ 2024-06-26

每月下载量 4,784
用于 6 个crate(5 个直接使用)

Apache-2.0 OR MIT

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;
    }
}

但这需要不安全,并且非常难以使用。

最终目标是

  1. 安全性。我们应该能够在没有不安全的情况下创建和使用这种固定类型。(显然,固定类型本身仍然是不安全的实现)。
  2. 易用性。语法不应该与常规Rust差异太大。
  3. 可聚合性。包含多个固定类型的结构体可以安全地一起创建和初始化。
  4. 无隐式分配。在初始化期间不应需要分配。用户应该能够指定是在box中还是栈上初始化。
  5. 可失败性。不对初始化的成功性做出假设。

解决方案:pin_init

此库提供了类型 PinUninitInitResult 作为安全固定初始化的原语。有关这些类型的详细信息可以在各自的文档中找到,但简而言之,与普通非固定类型的(可能失败的)构造函数不同,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,
    }),
}));

此库还提供了 UniqueRcUniqueArc,灵感来自 servo_arc。它们可以在共享之前可变地初始化 RcArc。提供了 Rc::pin_withArc::pin_with,它们内部创建 UniqueRcUniqueArc,使用给定的构造函数进行固定初始化,并将它们转换为可共享的形式。

此库允许安全地初始化固定数据结构。可以使用 pin-project 安全地访问这些结构体。你可以将 #[pin_init]#[pin_project] 与你的结构体一起使用,它们甚至共享相同的 #[pin] 字段属性!

请参阅 示例 以了解一些非人工示例。

依赖关系

~1.5MB
~35K SLoC