2 个版本
0.0.2 | 2019年10月12日 |
---|---|
0.0.1 | 2019年10月12日 |
在 Rust 模式 中排名第 1925
每月下载量 23
用于 2 crates
34KB
450 行
stackpin
此数据包公开了一个 StackPinned
类型,允许在声明时将 !Unpin
数据固定到栈上。
为此,此数据包提供了一个 FromUnpinned
特性和一个 stack_let!
宏,使创建 PinStack
对象(简称为 PinStack
)更加安全。
在声明时获取固定实例就像这样简单
stack_let!(unmovable = Unmovable::new_unpinned("Intel the Beagle")); // this creates the unmovable instance on the stack and binds `unmovable` with a `PinStack<Unmovable>`
对于 Unmovable
结构体实现 FromUnpinned<String>
特性。
lib.rs
:
stackpin
数据包公开了一个 StackPinned
类型,允许在声明时将 !Unpin
数据固定到栈上。数据包公开了一个特性,FromUnpinned
,以及一个宏 stack_let
,使创建 StackPinned
实例更加容易。数据包还公开了 PinStack
类型别名,用于 Pin<StackPinned<T>>
。
这个crate受到了pin-utils crate的启发,主要区别在于
- pin-utils 提供了一个宏,用于返回一个
Pin<&mut T>
实例,具有包括重新借用在内的“可变引用”语义。而stackpin
crate推广了“根句柄”语义,保证一个消耗PinStack<T>
的函数只消耗 唯一 的T
句柄,而不是一个重新借用的引用。 stack_let!(mut id : ty = expr)
宏的语法试图模仿一个普通的let mut id : ty = expr
语句。- 提供的
FromUnpinned
特性和Unpinned
结构体旨在将不可移动的类型与可以用来构建它们的 数据分开。stackpin
旨在推广一个模型,其中所有不可移动的类型只能通过加锁一次来访问。 StackPinned<T>
类型表达了对T
的析构函数将被运行的强烈保证。stackpin
crate 专注于堆栈锁定。 pin-utils crate 还提供了其他工具,如锁定投影。
可堆栈锁定类型
想要从 StackPinned
提供的保证中受益的类型 T 应该是 !Unpin
。这是强制执行“将运行释放”保证所必需的。
此外,stackpin
crate 推广了一种习惯用法,即“不可移动”类型与可移动类型严格分离,并且最好只能通过 PinStack
来访问。
例如,让我们考虑以下 Unmovable
结构体(来自 pin 模块的文档)
use std::marker::PhantomPinned;
use std::ptr::NonNull;
struct Unmovable {
// Owned data
s: String,
// Self referential pointer meant to point to `s`
slice: NonNull<String>,
// Obligatory marker that makes this struct `!Unpin`.
// Without this, implementing `FromUnpinned` for `Unmovable` would not be safe.
_pinned: PhantomPinned,
}
重要的是要注意,这个结构体本身并不是不可移动的,因为在 Rust 中没有这样的类型。相反,我们将通过隐私来强制执行这一点:由于该结构体的字段是私有的,因此不能在模块外部创建其实例。同样,不应提供任何公共的“构造函数”函数 pub fn new() -> Unmovable
。
那么,客户端应该如何消费 Unmovable
实例呢?
使用 stackpin
的推荐解决方案是为 Unmovable
实现 FromUnpinned<Data>
,其中 Data
是通常用作“构造函数”函数参数的类型。
use stackpin::FromUnpinned;
// An `Unmovable` can be created from a `String`
unsafe impl FromUnpinned<String> for Unmovable {
// This associated type can be used to retain information between the creation of the instance and its pinning.
// This allows for some sort of "two-steps initialization" without having to store the initialization part in the
// type itself.
// Here, we don't need it, so we just set it to `()`.
type PinData = ();
// Simply builds the Unmovable from the String.
// The implementation of this function is not allowed to consider that the type won't ever move **yet**.
// (in particular, the `Self` instance is returned by this function)
// Note, however, that safe users of FromUnpinned will:
// * Not do anything to with the returned `Self` instance between the call to
// `from_unpinned` and the call to `on_pin`.
// * Not panic between calling the two functions
// * Always call the second function if the first has been called.
unsafe fn from_unpinned(s: String) -> (Self, ()) {
(
Self {
s,
// We will "fix" this dangling pointer once the data will be pinned
// and guaranteed not to move anymore.
slice: NonNull::dangling(),
_pinned: PhantomPinned,
},
(),
)
}
// Performs a second initialization step on an instance that is already guaranteed to never move again.
// This allows to e.g. set self borrow with the guarantee that they will remain valid.
unsafe fn on_pin(&mut self, _data: ()) {
// Data will never move again, set the pointer to our own internal String whose address
// will never change anymore
self.slice = NonNull::from(&self.s);
}
}
实现了 FromUnpinned<Data>
对 T
的支持后,现在可以添加一个返回 Unpinned<Data, T>
的 "构造函数"。Unpinned<U, T>
结构体是围绕 U
的简单辅助结构体,它维护目标类型 T
。这被 stack_let
宏用于推断用户可能想要产生的 T
类型。
impl Unmovable {
fn new_unpinned<T: Into<String>>(s: T) -> Unpinned<String, Unmovable> {
Unpinned::new(s.into())
}
}
然后,Unmovable
结构体的用户可以通过使用 stack_let
宏简单地构建一个实例。
use stackpin::stack_let;
// ...
stack_let!(unmovable = Unmovable::new_unpinned("Intel the Beagle")); // this creates the unmovable instance on the stack and binds `unmovable` with a `PinStack<Unmovable>`
// ...