1 个不稳定版本
0.1.0 | 2020年12月27日 |
---|
在 无标准库 中排名第 165
每月下载量 837 次
32KB
463 代码行
drop-move
此 crate 允许将 Drop
实现为 "通过移动传递"。以下是如何使用它从 drop
中调用 FnOnce
的示例。
use drop_move::{drop_move_wrap, DropMove, DropHandle};
drop_move_wrap! {
/// Runs a function when dropped.
#[derive(Clone)]
pub struct DropGuard<F: FnOnce()>(DropGuardInner {
func: F,
});
}
impl<F: FnOnce()> DropMove for DropGuardInner<F> {
fn drop_move(self_: DropHandle<Self>) {
(DropHandle::into_inner(self_).func)()
}
}
impl<F: FnOnce()> DropGuard<F> {
pub fn new(f: F) -> Self {
DropGuardInner { func: f }.into()
}
}
let mut x: u32 = 0;
{
let y = Box::new(&mut x); // Box is not Copy, so the closure will only be FnOnce.
let guard = DropGuard::new(move || **y += 1);
}
assert_eq!(x, 1);
通过实现 DropMove
trait,我们能够在 DropGuard
超出作用域时运行 func
。通常的 Drop
trait 只允许 drop(&mut self)
,这不允许移动 DropGuard
的成员,这是调用 FnOnce
所必需的。它们不允许 drop(self)
的原因是,这会导致意外地最终导致自我丢弃,从而引发无限循环。根据 Rust 的常规语义,self
将在作用域结束时被丢弃,即使为 drop(self)
添加了特殊情况,它仍然容易在由 drop
调用的函数中发生。
通过将 self
包装在 DropHandle
中,可以避免这些问题,该 DropHandle
会在结构体的每个成员超出作用域时才丢弃每个成员,而不是递归地调用 drop
。从语义上看,drop_move
可以被视为解构 DropGuard
。每个未移动的成员将在它超出作用域时被丢弃。这些成员可以通过 into_inner
通过值访问。原始的 DropGuard
可以通过 into_outer
获取,但使用此功能时必须小心避免无限递归。
从这种解构视角来看,drop_move
也支持解构(通常情况下,实现 Drop
接口的数据类型不允许解构),这并不奇怪。在这里,我们可以将 DropGuard
转换回包含的函数。
impl<F: FnOnce()> DropGuard<F> {
/// Extract the function.
pub fn into_inner(self) -> F {
let inner: DropGuardInner<F> = self.into();
inner.func
}
}
其工作原理是 drop_move_wrap!
展开成两个结构定义。
/// Runs a function when dropped.
#[derive(Clone)]
pub struct DropGuard<F: FnOnce()>(DropMoveWrapper<DropGuardInner<F>>);
/// Runs a function when dropped.
#[derive(Clone)]
struct DropGuardInner<F: FnOnce()> {
func: F,
};
外部结构 DropGuard
提供了公共接口,而 DropGuardInner
包含了 struct
的实际成员。两者都不会实现 Drop
。相反,DropMoveWrapper
将基于您提供的 DropMove
实现 Drop
。结构成员可以通过 &self.0.func
从 DropGaurd
借用,因为 DropMoveWrapper
实现了 Deref
。它们可以通过将 DropGaurd
转换为 DropGuardInner
来移动,使用 DropMoveWrapper::into_inner(self.0)
或 self.into()
。
请注意,这两个结构的文档注释和属性已经被复制。实际上,文档注释被编译器视为 属性。
该宏还创建了一些特质实现。
impl<F: FnOnce()> From<DropGuard<F>> for DropGuardInner<F> {
fn from(x: DropGuard<F>) -> Self {
DropMoveWrapper::into_inner(x.0)
}
}
impl<F: FnOnce()> From<DropGuardInner<F>> for DropGuard<F> {
fn from(x: DropGuardInner<F>) -> Self {
Self(DropMoveWrapper::new(x))
}
}
impl<F: FnOnce()> DropMoveTypes for DropGuardInner<F>
{
type Outer = DropGuard<F>;
}
DropMoveTypes
的实现让 DropMoveWrapper
和 DropHandle
了解 DropGuard
和 DropGuardInner
之间的关系。它在内部结构上实现,因为这将在内部结构是私有的但外部是公共的常见情况下保持实现私有。From
的实现是为了知道如何相互转换,并作为创建和解构 DropGuard
的便捷方法。
你可能想知道为什么 drop_move
使用 DropHandle
而不是直接传递内部结构 DropGuardInner
(这会对解构和逐个删除成员的行为正确),然而,你不太可能直接调用一个 &self
或 &mut self
函数,这些函数需要一个 DropGuard
的实例。这需要重新构建 DropGuard
以便可以借用,然后在调用之后仔细解构以避免无限 drop
递归。 DropHandle
允许你避免这种容易出错的构造,因为它为外部结构实现了 Deref
,因此你可以直接调用其方法。
有关宏的完整支持的语法,请参阅 drop_move_wrap!
的文档。有关完整的示例,请参阅 DropGuard
的源代码。