Updated 'src/smart_pointers/drop-t'.

This commit is contained in:
Hector PENG
2026-04-03 18:50:08 +08:00
parent 07caa30b15
commit 2062138bd3
5 changed files with 101 additions and 107 deletions

View File

@@ -0,0 +1,6 @@
[package]
name = "drop-example"
version = "0.1.0"
edition = "2024"
[dependencies]

View File

@@ -0,0 +1,18 @@
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println! ("正在以数据 `{}` 弃用 CustomSmartPointer", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("我的事情"),
};
println! ("CustomSmartPointer 实例已创建");
drop(c);
println! ("CustomSmartPointer 在 main 结束前被弃用");
}

View File

@@ -1,8 +0,0 @@
[package]
name = "drop_func_demo"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -1,19 +0,0 @@
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println! ("正在使用数据 `{}` 弃用 CustomSmartPointer", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("我的事情"),
};
println! ("已创建出一个 CustomSmartPointer 实例。");
drop(c);
println! ("在 main 结束之前这个 CustomSmartPointer 已被弃用。")
}

View File

@@ -1,18 +1,16 @@
# 使用 `Drop` 特质运行清理代码
# 通过 `Drop` 特质在清理时运行代码
**Running Code on Cleanup with `Drop` Trait**
对于灵巧指针模式而言,第二个重要的特质是 `Drop`,他允许咱们定制在值即将超出作用域时发生的事情。咱们可以为任何类型上的 `Drop` 特质提供实现,并且该代码可用于释放文件或网络连接等资源。
咱们之所以在灵巧指针的语境下介绍 `Drop` 特质,是因为在实现灵巧指针时,`Drop` 特质的功能几乎总是会用到。例如,当某个 `Box<T>` 被弃用时,他将解分配(释放)匣子指向的堆上空间。
对于灵巧指针模式来讲,第二个重要的特质是 `Drop`,他允许咱们定制某个值即将超出作用域时发生的事情。咱们可以在任何类型上,为 `Drop` 特质提供一个实现,实现代码可用于释放文件或网络连接等资源
在某些语言中,对于某些类型,程序员必须在每次使用完这些类型的实例后,调用代码释放内存或资源。示例包括文件句柄、套接字以及锁等。如果程序员忘记了,那么系统会过载并崩溃。在 Rust 中,咱们可以指定每当值超出作用域时要运行的一段特定代码,编译器将自动插入这段代码。因此,咱们无需小心地在程序中特定类型的实例结束的每个地方,都放置清理代码 -- 咱们仍然不会泄露资源!
咱们之所以在灵巧指针上下文中引入 `Drop` 特质,是由于 `Drop` 特质的功能在实现某个灵巧指针时几乎都会用到。比如,当 `Box<T>` 被弃用时,他将释放该匣子指向的堆上的内存空间
咱们通过实现 `Drop` 特质,指定值超出作用域时要运行的代码。`Drop` 特质要求咱们实现一个名为 `drop` 的方法,该方法取对 `self` 的可变引用。为了了解 Rust 何时会调用 `drop`,现在我们来以 `println!` 语句实现 `drop`
在某些语言中对于某些类型程序员必须在每次使用完这些类型的实例后调用代码来释放内存或资源。这方面的例子包括文件句柄、套接字或锁file handles, sockets, or locks。如果他们忘记了系统可能会变得过载并崩溃。在 Rust 中,咱们可以指定在值超出范围时运行一段特定的代码,编译器将自动插入这段代码。因此,咱们无需小心地将清理代码,放在程序中某个特定类型的实例完成后的任何地方 -- 咱们仍然不会泄露资源!
咱们通过实现 `Drop` 特质,来指定当一个值超出作用域时要运行的代码。`Drop` 特质要求咱们实现一个名为 `drop` 的方法,他需要一个对 `self` 的可变引用。为了了解Rust 何时调用 `drop`,现在咱们就来用 `println!` 语句实现 `drop`
下面清单 15-14 给出了仅有着一项定制功能,即在其实例超出作用域时打印出 `正在弃用 CustomSmartPointer` 的一个 `CumstomSmartPointer` 结构体,以展示出 Rust 在何时运行这个 `drop` 函数。
下面清单 15-14 展示了个 `CustomSmartPointer` 结构体,其唯一的定制功能是,他将在实例超出作用域时打印 `正在弃用 CustomSmartPointer`,以展示 Rust 会于何时运行 `drop` 函数。
<a name="listing_15-14"></a>
文件名:`src/main.rs`
```rust
@@ -22,51 +20,91 @@ struct CustomSmartPointer {
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println! ("正在使用数据 `{}` 弃用 CustomSmartPointer", self.data);
println! ("正在数据 `{}` 弃用 CustomSmartPointer", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("c - 我的事情"),
data: String::from("我的事情"),
};
let d = CustomSmartPointer {
data: String::from("d - 其他事情"),
data: String::from("其他事情"),
};
println! ("已创建出一些 CustomSmartPointer 实例");
println! ("一些 CustomSmartPointer 实例已创建");
}
```
*清单 15-14:一个实现了 `Drop` 特质的 `CustomSmartPointer` 结构体,咱们将把咱们的清理代码放在这里*
**清单 15-14**`CustomSmartPointer` 结构体,实现了 `Drop` 特质,我们将于其中放置清理代码
`Drop` 特质包含在前奏中,included in the prelude所以我们不需要把他带入作用域。我们 `CustomSmartPointer` 实现 `Drop` 特质,并为 `drop` 方法提供一个调用 `println!` 的实现。`drop` 函数的主体是在咱们的类型的实例超出作用域时,打算运行的任何逻辑的地方。咱们在这里打印一些文本来直观地演示 Rust 何时调用 `drop`
`Drop` 特质包含在前奏中,因此我们无需带入他到作用域。我们 `CustomSmartPointer` 实现 `Drop` 特质,并为 `drop` 方法提供一个调用 `println!` 的实现。`drop` 方法的主体,是咱们将放置当咱们的类型的实例超出作用域时,咱们打算运行的任何逻辑之处。我们在这里打印一些文本来直观地演示 Rust 将于何时调用 `drop`
`main` 中,我们创建了两个 `CustomSmartPointer` 实例,然后打印`已创建出一些 CumstomSmartPointer 实例`。在 `main` 的结尾,我们的 `CustomSmartPointer` 实例将超出作用域Rust 将调用我们放在 `drop` 方法中的代码,打印我们的最终信息。注意,我们不需要显式地调用 `drop` 方法。
`main` 中,我们创建了两个 `CustomSmartPointer` 实例,然后打印 `一些 CustomSmartPointer 实例已创建`。在 `main` 结束处,我们的 `CustomSmartPointer` 实例将超出作用域Rust 将调用我们放在 `drop` 方法中的代码,打印我们的最终信息。注意,我们不需要显式地调用 `drop` 方法。
当我们运行这个程序时,我们会看到以下输出:
```console
$ cargo run
Compiling sp_demos v0.1.0 (/home/lennyp/rust-lang/sp_demos)
Finished dev [unoptimized + debuginfo] target(s) in 0.50s
Running `target/debug/sp_demos`
已创建出一些 CustomSmartPointer 实例
正在使用数据 `d - 其他事情` 弃用 CustomSmartPointer
正在使用数据 `c - 我的事情` 弃用 CustomSmartPointer
Compiling drop-example v0.1.0 (/home/hector/rust-lang-zh_CN/projects/drop-example)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s
Running `target/debug/drop-example`
一些 CustomSmartPointer 实例已创建
正在数据 `其他事情` 弃用 CustomSmartPointer
正在数据 `我的事情` 弃用 CustomSmartPointer
```
当我们的实例超出作用域Rust 自动为我们调用了 `drop`,从而调用我们指定的代码。变量的弃用顺序与其创建顺序相反,因此 `d` `c` 之前被弃用。这个例的目的是给咱们一个直观了解 `drop` 方法如何工作的直观指引;通常咱们会指定咱们类型需要运行的清理代码,而不是打印消息。
当我们的实例超出作用域Rust 自动为我们调用了 `drop`,从而调用我们指定的代码。变量按照他们创建的相反顺序被弃用,因此 `d` 先于 `c` 被弃用。这个例的目的是,给予咱们 `drop` 方法工作原理的直观指引;通常咱们会指定咱们类型需要的清理代码,而不是打印消息。
遗憾的是,禁用自动的 `drop` 功能并不简单。禁用 `drop` 通常并无必要;`Drop` 特质的核心,就在于他是自动处理的。不过,有时咱们会希望提前清理某个值。一个示例便是使用管理锁的灵巧指针:咱们可能打算强制调用释放锁的 `drop` 方法以便同一作用域内的其他代码可以获取该锁。Rust 不允许咱们手动调用 `Drop` 特质的 `drop` 方法;相反,当咱们打算在值的作用域结束前强制弃用该值时,咱们必须调用标准库提供的 `std::mem::drop` 函数。
## 使用 `std::mem::drop` 提前弃用值
如下清单 15-15 中所示,尝试通过修改清单 15-14 中的 `main` 函数,来手动调用 `Drop` 特质的 `drop` 方法是行不通的。
**Drop a Value Early with `std::mem::drop`**
<a name="listing_15-15"></a>
文件名:`src/main.rs`
```rust
fn main() {
let c = CustomSmartPointer {
data: String::from("一些数据"),
};
println! ("CustomSmartPointer 实例已创建");
c.drop();
println! ("CustomSmartPointer 在 main 结束前被弃用");
}
```
不幸的是,要禁用自动 `drop` 功能并不简单。通常情况下,禁用 `drop` 功能是没有必要的;`Drop` 特质的全部意义在于他是自动处理的。然而在少数情况下,咱们可能想要提前清理一个值。一个例子便是在运用管理锁的灵巧指针时:咱们可能想要强制使用释放锁的 `drop` 方法这样同一作用域内的其他代码就可以获得锁。Rust 不允许咱们手动调用 `Drop` 特质的 `drop` 方法;相反,如果咱们打算强制一个值在其作用域结束前被弃用,咱们必须调用标准库提供的 `std::mem::drop` 函数。
**清单 15-15**:尝试手动调用 `Drop` 特质`drop` 方法以提前清理
我们试图通过修改清单 15-14 中的 `main` 函数来手动调用 `Drop` 特质的 `drop` 方法如清单15-15所示,我们得到一个编译器报错:
我们尝试编译这段代码时,我们得到下面这个报错:
```console
$ cargo run
Compiling drop-example v0.1.0 (/home/hector/rust-lang-zh_CN/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 - c.drop();
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
```
这个报错指出,我们不允许显式调用 `drop`。这条错误信息使用了术语 *析构函数*,这是用于清理实例的函数的通用编程术语。*析构函数* 好比 *构造函数*后者创建实例。Rust 中的 `drop` 函数属于一个特殊的析构函数。
Rust 之所以不允许我们显式地调用 `drop`,是因为 Rust 仍将在 `main` 函数结束处自动对值调用 `drop`。这会导致 *双重释放* 的错误,因为 Rust 会尝试清理同一个值两次。
我们无法禁用值超出作用域时 `drop` 的自动插入,并且无法显式调用 `drop` 方法。因此,当我们需要强制某个值被提前清理时,我们要使用 `std::mem::drop` 函数。
`std::mem::drop` 函数不同于 `Drop` 特质中的 `drop` 方法。我们通过作为参数传递我们打算强制弃用的值来调用他。这个函数位于前奏中,因此我们可以修改清单 15-15 中的 `main` 来调用 `drop` 函数,如下清单 15-16 中所示:
<a name="listing_15-16"></a>
文件名:`src/main.rs`
```rust
@@ -75,73 +113,32 @@ fn main() {
data: String::from("一些数据"),
};
println! ("已创建出一个 CustomSmartPointer 实例。");
c.drop();
println! ("在 main 结束之前这个 CustomSmartPointer 已被弃用。")
}
```
*清单 15-15尝试调用 `Drop` 特质的 `drop` 方法来提前清理*
当我们试图编译这段代码时,我们会得到这样的报错:
```console
$ cargo run
Compiling sp_demos v0.1.0 (/home/lennyp/rust-lang/sp_demos)
error[E0040]: explicit use of destructor method
--> src/main.rs:17:7
|
17 | c.drop();
| --^^^^--
| | |
| | explicit destructor calls not allowed
| help: consider using `drop` function: `drop(c)`
For more information about this error, try `rustc --explain E0040`.
error: could not compile `sp_demos` due to previous error
```
这个错误信息指出,我们不允许显式调用 `drop`。这条错误信息使用了术语 “解构函数destructor”这是清理实例的函数的通用编程术语。解构函数类似于 *构造函数constructor*后者创建一个实例。Rust 中的 `drop` 函数就是一个特殊的解构函数。
Rust 之所以不允许咱们显式地调用 `drop`,是因为 Rust 仍然会在 `main` 函数结尾处自动调用值上的 `drop`,这将导致 *双重释放double free* 的错误,由于 Rust 会试图对同一个值进行两次清理。
当值超出作用域时,我们无法禁用 `drop` 的自动插入,也无法显式调用 `drop` 方法。所以,如果我们需要强制一个值提前被清理,我们就使用 `std::mem::drop` 函数。
`std::mem::drop` 函数与 `Drop` 特质中的 `drop` 方法不同。咱们通过把咱们想要强制弃用的值作为参数传递来调用他。这个函数在前奏中,所以我们可以修改清单 15-15 中的 `main` 来调用 `drop` 函数,如清单 15-16 所示:
文件名:`src/main.rs`
```rust
fn main() {
let c = CustomSmartPointer {
data: String::from("我的事情"),
};
println! ("已创建出一个 CustomSmartPointer 实例。");
drop(c);
println! ("在 main 结束之前这个 CustomSmartPointer 已被弃用。")
}
```
*清单 15-16调用 `std::mem::drop` 在值超出作用域前,显式地弃用该值*
**清单 15-16**:调用 `std::mem::drop` 在值超出作用域前,显式地弃用该值
运行这段代码将打印以下内容:
运行这段代码将打印以下内容:
```console
$ cargo run
Compiling sp_demos v0.1.0 (/home/lennyp/rust-lang/sp_demos)
Finished dev [unoptimized + debuginfo] target(s) in 0.40s
Running `target/debug/sp_demos`
已创建出一个 CustomSmartPointer 实例
正在使用数据 `一些数据` 弃用 CustomSmartPointer!
在 main 结束之前这个 CustomSmartPointer 被弃用
Compiling drop-example v0.1.0 (/home/hector/rust-lang-zh_CN/projects/drop-example)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s
Running `target/debug/drop-example`
CustomSmartPointer 实例已创建
正在数据 `一些数据` 弃用 CustomSmartPointer
CustomSmartPointer 在 main 结束前被弃用
```
文本 ``正在使用数据 `一些数据` 弃用 CustomSmartPointer!`` 打印在 `已创建出一个 CustomSmartPointer 实例` 与 `在 main 结束之前这个 CustomSmartPointer 被弃用` 之间,显示 `drop` 方法在这个时间点被调用弃用 `c`。
文本 ``正在数据 `一些数据` 弃用 CustomSmartPointer`` 打印在 `CustomSmartPointer 实例已创建` 与 `CustomSmartPointer 在 main 结束前被弃用` 之间,表明 `drop` 方法的代码于该处被调用弃用 `c`。
咱们可以通过多种方式使用 `Drop` 特质实现中指定的代码,方便安全地进行清理:例如,咱们可以用他创建咱们自己的内存分配器! 有了 `Drop` 特质和 Rust 的所有权系统,咱们不需要记得清理内存,因为 Rust 会自动完成。
咱们可以多种方式使用指定于 `Drop` 特质实现中的代码,以使资源清理方便安全:例如,咱们可以用他创建自己的内存分配器! `Drop` 特质和 Rust 的所有权系统,咱们不记得清理资源,因为 Rust 会自动完成。
咱们也不必担心因意外清理仍在使用的值而导致的问题:确保引用始终有效的所有权系统,还确保在值不再被使用时,`drop` 只被调用一次。
咱们也不必担心因意外清理仍在使用的值而导致的问题:确保引用始终有效的所有权系统,同时还保证 `drop` 只会在值不再被使用时被调用一次。
现在我们已经研究了 `Box<T>` 和灵巧指针的一些特性,让我们看看标准库中定义的其他几个灵巧指针。
现在我们已经探讨了 `Box<T>` 和灵巧指针的一些特征,我们看看定义在标准库中的其他几个灵巧指针。
End