#drop #move #moving #pass #instance #dropped #dropping

无 std drop-move

一个支持从 drop 中移出的 Rust 库

1 个不稳定版本

0.1.0 2020年12月27日

无标准库 中排名第 165

Download history 254/week @ 2024-04-09 120/week @ 2024-04-16 239/week @ 2024-04-23 162/week @ 2024-04-30 38/week @ 2024-05-07 177/week @ 2024-05-14 267/week @ 2024-05-21 316/week @ 2024-05-28 471/week @ 2024-06-04 506/week @ 2024-06-11 261/week @ 2024-06-18 219/week @ 2024-06-25 247/week @ 2024-07-02 153/week @ 2024-07-09 155/week @ 2024-07-16 245/week @ 2024-07-23

每月下载量 837

MIT 许可证 MIT

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.funcDropGaurd 借用,因为 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 的实现让 DropMoveWrapperDropHandle 了解 DropGuardDropGuardInner 之间的关系。它在内部结构上实现,因为这将在内部结构是私有的但外部是公共的常见情况下保持实现私有。From 的实现是为了知道如何相互转换,并作为创建和解构 DropGuard 的便捷方法。

你可能想知道为什么 drop_move 使用 DropHandle 而不是直接传递内部结构 DropGuardInner(这会对解构和逐个删除成员的行为正确),然而,你不太可能直接调用一个 &self&mut self 函数,这些函数需要一个 DropGuard 的实例。这需要重新构建 DropGuard 以便可以借用,然后在调用之后仔细解构以避免无限 drop 递归。 DropHandle 允许你避免这种容易出错的构造,因为它为外部结构实现了 Deref,因此你可以直接调用其方法。

有关宏的完整支持的语法,请参阅 drop_move_wrap! 的文档。有关完整的示例,请参阅 DropGuard 的源代码。

无运行时依赖