参考章节《Rust 程序设计语言》第15.3章 使用 Drop Trait 运行清理代码
对于智能指针模式来说第二个重要的 trait
是 Drop
,其允许我们在值要离开作用域时执行一些代码。一些语言也把这个东西叫做 析构函数
Drop trait
要求实现一个叫做 drop
的方法,它获取一个 self
的可变引用。
下面是书上的例子,我觉得有点啰嗦,不过将就看吧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
struct CustomSmartPointer {
data: String,
}
// 实现 Drop trait
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.");
}
|
当运行这个程序,输出如下
1
2
3
4
5
6
7
|
$ cargo run
Compiling drop-example v0.1.0 (file:///projects/drop-example)
Finished dev [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`!
|
通过 std::mem::drop 提早丢弃值
有些情况下,你可能需要提前调用 drop
,而不是等到它离开作用域时自动调用
比如当使用智能指针管理锁时;你可能希望强制运行 drop
方法来释放锁以便作用域中的其他代码可以获取锁。
不幸的是,Rust 并不允许我们主动调用 Drop trait
的 drop
方法
1
2
3
4
5
6
7
8
9
10
|
fn main() {
let c = CustomSmartPointer {
data: String::from("some data"),
};
println!("CustomSmartPointer created.");
c.drop(); // 这里主动调用 drop 方法
println!("CustomSmartPointer dropped before the end of main.");
}
|
当运行这段代码时,会报错
1
2
3
4
5
6
7
8
9
10
11
12
13
|
$ 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: `drop(c)`
For more information about this error, try `rustc --explain E0040`.
error: could not compile `drop-example` due to previous error
|
Rust 不允许我们显式调用 drop
因为 Rust 仍然会在 main
的结尾对值自动调用 drop
,这会导致一个 double free
错误,因为 Rust 会尝试清理相同的值两次。
为此,我们可以使用 std::mem::drop
,不同于 Drop trait
中的 drop
方法。std::mem::drop
可以通过传递希望提早强制丢弃的值作为参数。
std::mem::drop
位于 prelude
,因此可以直接使用
1
2
3
4
5
6
7
8
9
10
|
fn main() {
let c = CustomSmartPointer {
data: String::from("some data"),
};
println!("CustomSmartPointer created.");
drop(c); // 这样就可以了
println!("CustomSmartPointer dropped before the end of main.");
}
|
运行这段代码,输出如下
1
2
3
4
5
6
7
|
$ cargo run
Compiling drop-example v0.1.0 (file:///projects/drop-example)
Finished dev [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.
|
从结果来看,我们调用 drop
提前清理后,在离开作用域后,Rust 就不会再调用 c.drop
了
但其实不然,当调用 drop
时,我猜这里 c
就已经算离开作用域了,因为 c
的所有权移动到 drop
里了,不过这是我猜的,不过应该我是对的,哈哈哈