7 个版本 (破坏性更新)
0.6.0 | 2023年6月15日 |
---|---|
0.5.1 | 2022年8月25日 |
0.5.0 | 2022年3月28日 |
0.4.0 | 2022年1月26日 |
0.1.0 | 2021年4月13日 |
#198 in Rust 模式
10,973 每月下载量
在 35 个crate中使用了(直接使用4个)
87KB
1K SLoC
moveit
一个用于安全、就地构建 Rust(和 C++!)对象的库。
工作原理
moveit
围绕 unsafe trait
展开,这些 trait 对 !Unpin
类型施加额外的保证,使得它们可以在 C++ 的意义上进行移动。经常使用两种“移动”的含义
- Rust 的意义,它类似于盲目的 memcpy,类似于 C++ 的 “std::is_trivially_moveable` 类型 trait。Rust 的移动也会使移动后的对象变得不可访问。
- C++ 的意义,其中移动实际上类似于一个可变
Clone
操作,这使得移动后的值在作用域结束时仍然可访问,以便被销毁。
C++ 还有 构造函数,这是特殊的函数,可以在特定位置生成新的值。特别是,C++ 构造函数可能假设 *this
的地址不会改变;所有 C++ 对象都是有效地固定的,并且必须使用复制或移动构造函数来创建新对象。
【New
】,CopyNew
,和 MoveNew
trait 将这些概念引入 Rust。一个 [ New
] 类似于一个 nilary FnOnce
,除了它不返回其结果,而是将其写入一个 Pin<&mut MaybeUninit<T>>
,它处于 Pin
文档中描述的“内存可能被重新分配”状态(即它刚刚被分配或最近运行了析构函数)。这允许一个 [ New
] 依赖于指针地址保持稳定,就像 C++ 中的 *this
一样。
实现了 CopyNew
的类型可以进行 复制构造:给定指向 T: CopyNew
的任意指针,我们可以生成一个构造函数,在指定位置构造一个全新的、相同的 T
。实现了 MoveNew
的类型可以进行 移动构造:给定指向 T
的 拥有者 指针(参见 DerefMove
),我们可以生成类似的构造函数,但它还会销毁 T
和拥有者指针的存储。
以上行为并不违反现有的 Pin
保证:从 Pin<P>
中移出并不执行 Rust 意义上的移动,而是在 C++ 意义上进行移动:通过被固定的指针以安全的方式修改,以构造一个新的 P::Target
,然后销毁指针及其内容。
一般来说,可移动构造的类型将希望是 !Unpin
,以便它们可以自我引用。自我引用类型是移动构造函数的主要动机之一。
构造函数
任何实现了 [New
] 的类型都可以视为构造函数。构造函数类似于有保证的 RVO 的闭包,可以用于原地构造自我引用类型。以下是从 Pin<T>
文档中的例子
use std::marker::PhantomPinned;
use std::mem::MaybeUninit;
use std::pin::Pin;
use std::ptr;
use std::ptr::NonNull;
use moveit::new;
use moveit::new::New;
use moveit::moveit;
// This is a self-referential struct because the slice field points to the
// data field. We cannot inform the compiler about that with a normal
// reference, as this pattern cannot be described with the usual borrowing
// rules. Instead we use a raw pointer, though one which is known not to be
// null, as we know it's pointing at the string.
struct Unmovable {
data: String,
slice: NonNull<String>,
_pin: PhantomPinned,
}
impl Unmovable {
// Defer construction until the final location is known.
fn new(data: String) -> impl New<Output = Self> {
new::of(Unmovable {
data,
// We only create the pointer once the data is in place
// otherwise it will have already moved before we even started.
slice: NonNull::dangling(),
_pin: PhantomPinned,
}).with(|this| unsafe {
let this = this.get_unchecked_mut();
this.slice = NonNull::from(&this.data);
})
// It is also possible to use other `new::` helpers, such as
// `new::by` and `new::by_raw`, to configure construction behavior.
}
}
// The constructor can't be used directly, and needs to be emplaced.
moveit! {
let unmoved = Unmovable::new("hello".to_string());
}
// The pointer should point to the correct location,
// so long as the struct hasn't moved.
// Meanwhile, we are free to move the pointer around.
let mut still_unmoved = unmoved;
assert_eq!(still_unmoved.slice, NonNull::from(&still_unmoved.data));
// Since our type doesn't implement Unpin, this will fail to compile:
// let mut new_unmoved = Unmovable::new("world".to_string());
// std::mem::swap(&mut *still_unmoved, &mut *new_unmoved);
// However, we can implement `MoveNew` to allow it to be "moved" again.
[new
] 模块提供了各种辅助工具,用于制作构造函数。一般来说,在 Rust 中通常构建并返回值的函数应该返回 impl New
。这与拥有 async fn
和 .iter()
函数类似。
嵌入
上面的例子使用了 [moveit!()
] 宏,这是将构造函数转换为值的方法之一。 moveit
为运行构造函数提供了两种选择
例如,我们可以通过编写 Box::emplace(Unmovable::new())
将上述内容放入 Box
中。
许可证:Apache-2.0
这不是官方支持的产品。
依赖项
~185KB