使用 Drop 特征在清理时运行代码

对智能指针模式很重要的第二个特征是 ,它允许 您可以自定义当值即将超出范围时发生的情况。您可以 为任何类型的 trait 提供实现,并且该代码可以 用于释放文件或网络连接等资源。DropDrop

我们在智能指针的上下文中引入,因为 在实现 trait 的 智能指针。例如,当 a 被丢弃时,它将释放 box 指向的堆上的空间。DropDropBox<T>

在某些语言中,对于某些类型,程序员必须调用代码来释放内存 或 resources。例子 包括文件句柄、套接字或锁。如果他们忘记了,系统可能会 变得超负荷并崩溃。在 Rust 中,你可以指定特定的 每当值超出范围时运行代码,编译器将 此代码自动。因此,您无需小心 将清理代码放置在程序中任何位置的特定 type 已完成 - 您仍然不会泄漏资源!

通过实现 trait,您可以指定要在值超出范围时运行的代码。该 trait 要求您实现一个名为 的方法,该方法采用对 .要查看 Rust 何时调用 , 现在让我们实现 With 语句。DropDropdropselfdropdropprintln!

示例 15-14 显示了一个结构体,其唯一的自定义 功能是,它将在 实例超出范围,以显示 Rust 何时运行函数。CustomSmartPointerDropping CustomSmartPointer!drop

文件名: src/main.rs

struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        println!("Dropping CustomSmartPointer with data `{}`!", self.data);
    }
}

fn main() {
    let c = CustomSmartPointer {
        data: String::from("my stuff"),
    };
    let d = CustomSmartPointer {
        data: String::from("other stuff"),
    };
    println!("CustomSmartPointers created.");
}

示例 15-14:一个 CustomSmartPointer 结构体 实现 Drop trait,我们将把清理代码放到这个位置

该 trait 包含在 prelude 中,因此我们不需要将其引入 范围。我们在 上实现 trait 并提供一个 调用的方法的实现。函数的主体是放置要运行的任何逻辑的位置 类型的实例超出范围。我们在此处打印一些文本以 直观地演示 Rust 何时调用 .DropDropCustomSmartPointerdropprintln!dropdrop

在 中,我们创建 的两个实例,然后打印 。在 的末尾,我们的实例将超出范围,Rust 将调用我们放置 在 Method 中,打印我们的最终消息。请注意,我们不需要 显式调用该方法。mainCustomSmartPointerCustomSmartPointers createdmainCustomSmartPointerdropdrop

当我们运行这个程序时,我们将看到以下输出:

$ cargo run
   Compiling drop-example v0.1.0 (file:///projects/drop-example)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.60s
     Running `target/debug/drop-example`
CustomSmartPointers created.
Dropping CustomSmartPointer with data `other stuff`!
Dropping CustomSmartPointer with data `my stuff`!

当我们的实例超出范围时,Rust 会自动调用我们, 调用我们指定的代码。变量的删除顺序与 他们的创造,所以在 .此示例的目的是 为您提供该方法如何工作的视觉指南;通常你会 指定您的类型需要运行的清理代码,而不是 print 消息。dropdcdrop

使用 std::mem::d rop 提前删除值

不幸的是,禁用自动功能并不简单。禁用通常不是必需的;trait 的全部意义在于它会自动得到处理。然而,偶尔, 您可能希望尽早清理值。一个例子是使用 smart 管理锁的指针:您可能希望强制 释放锁,以便同一范围内的其他代码可以获取锁。 Rust 不允许你手动调用 trait 的方法;相反 您必须调用 Standard Library 提供的函数 如果要强制在其范围结束之前删除值。dropdropDropdropDropdropstd::mem::drop

如果我们尝试通过修改示例 15-14 中的函数来手动调用 trait 的方法,如示例 15-15 所示,我们将得到一个 编译器错误:Dropdropmain

文件名: src/main.rs

struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        println!("Dropping CustomSmartPointer with data `{}`!", self.data);
    }
}

fn main() {
    let c = CustomSmartPointer {
        data: String::from("some data"),
    };
    println!("CustomSmartPointer created.");
    c.drop();
    println!("CustomSmartPointer dropped before the end of main.");
}

示例 15-15:尝试从 Drop trait 以尽早清理

当我们尝试编译此代码时,我们将收到此错误:

$ cargo run
   Compiling drop-example v0.1.0 (file:///projects/drop-example)
error[E0040]: explicit use of destructor method
  --> src/main.rs:16:7
   |
16 |     c.drop();
   |       ^^^^ explicit destructor calls not allowed
   |
help: consider using `drop` function
   |
16 |     drop(c);
   |     +++++ ~

For more information about this error, try `rustc --explain E0040`.
error: could not compile `drop-example` (bin "drop-example") due to 1 previous error

此错误消息指出,不允许我们显式调用 .这 错误消息使用术语 Destructor,这是通用的编程术语 对于清理实例的函数。析构函数类似于创建实例的构造函数。Rust 中的函数是一个 特定的析构函数。dropdrop

Rust 不允许我们显式调用,因为 Rust 仍然会 自动调用 末尾的值。这将导致 double free 错误,因为 Rust 会尝试清理相同的值 两次。dropdropmain

我们不能禁用 when a value out of 的自动插入 范围,并且我们不能显式调用该方法。所以,如果我们需要强制 一个要提前清理的值,我们使用该函数。dropdropstd::mem::drop

该函数与 trait 中的方法不同。我们通过将要强制 drop 的值作为参数传递来调用它。 该函数位于 prelude 中,因此我们可以在示例 15-15 中修改为 调用函数,如示例 15-16 所示:std::mem::dropdropDropmaindrop

文件名: src/main.rs

struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        println!("Dropping CustomSmartPointer with data `{}`!", self.data);
    }
}

fn main() {
    let c = CustomSmartPointer {
        data: String::from("some data"),
    };
    println!("CustomSmartPointer created.");
    drop(c);
    println!("CustomSmartPointer dropped before the end of main.");
}

示例 15-16:显式调用 std::mem::d rop 在值超出范围之前删除值

运行此代码将打印以下内容:

$ cargo run
   Compiling drop-example v0.1.0 (file:///projects/drop-example)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.73s
     Running `target/debug/drop-example`
CustomSmartPointer created.
Dropping CustomSmartPointer with data `some data`!
CustomSmartPointer dropped before the end of main.

打印文本 ,表示方法 code 被调用 在那个点下降。Dropping CustomSmartPointer with data `some data`!CustomSmartPointer created.CustomSmartPointer dropped before the end of main.dropc

您可以通过多种方式使用 trait 实现中指定的代码来 使清理方便和安全:例如,您可以使用它来创建您的 自己的内存分配器!使用 trait 和 Rust 的所有权系统,您可以 不必记得清理,因为 Rust 会自动进行清理。DropDrop

您也不必担心因意外而导致的问题 清理仍在使用的值:确保 OWNERSHIP SYSTEM references are always valid 还确保 that 在 该值不再被使用。drop

现在我们已经研究了 smart 的一些特征 pointers,让我们看看标准 图书馆。Box<T>

本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准