W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
ch15-03-drop.md
commit d44317c3122b44fb713aba66cc295dee3453b24b
對(duì)于智能指針模式來說第二個(gè)重要的 trait 是 Drop
,其允許我們?cè)谥狄x開作用域時(shí)執(zhí)行一些代碼??梢詾槿魏晤愋吞峁?nbsp;Drop
trait 的實(shí)現(xiàn),同時(shí)所指定的代碼被用于釋放類似于文件或網(wǎng)絡(luò)連接的資源。我們?cè)谥悄苤羔樕舷挛闹杏懻?nbsp;Drop
是因?yàn)槠涔δ軒缀蹩偸怯糜趯?shí)現(xiàn)智能指針。例如,當(dāng) Box<T>
被丟棄時(shí)會(huì)釋放 box 指向的堆空間。
在其他一些語言中,我們不得不記住在每次使用完智能指針實(shí)例后調(diào)用清理內(nèi)存或資源的代碼。如果忘記的話,運(yùn)行代碼的系統(tǒng)可能會(huì)因?yàn)樨?fù)荷過重而崩潰。在 Rust 中,可以指定每當(dāng)值離開作用域時(shí)被執(zhí)行的代碼,編譯器會(huì)自動(dòng)插入這些代碼。于是我們就不需要在程序中到處編寫在實(shí)例結(jié)束時(shí)清理這些變量的代碼 —— 而且還不會(huì)泄漏資源。
指定在值離開作用域時(shí)應(yīng)該執(zhí)行的代碼的方式是實(shí)現(xiàn) Drop
trait。Drop
trait 要求實(shí)現(xiàn)一個(gè)叫做 drop
的方法,它獲取一個(gè) self
的可變引用。為了能夠看出 Rust 何時(shí)調(diào)用 drop
,讓我們暫時(shí)使用 println!
語句實(shí)現(xiàn) drop
。
示例 15-14 展示了唯一定制功能就是當(dāng)其實(shí)例離開作用域時(shí),打印出 Dropping CustomSmartPointer!
的結(jié)構(gòu)體 CustomSmartPointer
。這會(huì)演示 Rust 何時(shí)運(yùn)行 drop
函數(shù):
文件名: 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:結(jié)構(gòu)體 CustomSmartPointer
,其實(shí)現(xiàn)了放置清理代碼的 Drop
trait
Drop
trait 包含在 prelude 中,所以無需導(dǎo)入它。我們?cè)?nbsp;CustomSmartPointer
上實(shí)現(xiàn)了 Drop
trait,并提供了一個(gè)調(diào)用 println!
的 drop
方法實(shí)現(xiàn)。drop
函數(shù)體是放置任何當(dāng)類型實(shí)例離開作用域時(shí)期望運(yùn)行的邏輯的地方。這里選擇打印一些文本以展示 Rust 何時(shí)調(diào)用 drop
。
在 main
中,我們新建了兩個(gè) CustomSmartPointer
實(shí)例并打印出了 CustomSmartPointer created.
。在 main
的結(jié)尾,CustomSmartPointer
的實(shí)例會(huì)離開作用域,而 Rust 會(huì)調(diào)用放置于 drop
方法中的代碼,打印出最后的信息。注意無需顯式調(diào)用 drop
方法:
當(dāng)運(yùn)行這個(gè)程序,會(huì)出現(xiàn)如下輸出:
$ 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`!
當(dāng)實(shí)例離開作用域 Rust 會(huì)自動(dòng)調(diào)用 drop
,并調(diào)用我們指定的代碼。變量以被創(chuàng)建時(shí)相反的順序被丟棄,所以 d
在 c
之前被丟棄。這個(gè)例子剛好給了我們一個(gè) drop 方法如何工作的可視化指導(dǎo),不過通常需要指定類型所需執(zhí)行的清理代碼而不是打印信息。
不幸的是,我們并不能直截了當(dāng)?shù)慕?nbsp;drop
這個(gè)功能。通常也不需要禁用 drop
;整個(gè) Drop
trait 存在的意義在于其是自動(dòng)處理的。然而,有時(shí)你可能需要提早清理某個(gè)值。一個(gè)例子是當(dāng)使用智能指針管理鎖時(shí);你可能希望強(qiáng)制運(yùn)行 drop
方法來釋放鎖以便作用域中的其他代碼可以獲取鎖。Rust 并不允許我們主動(dòng)調(diào)用 Drop
trait 的 drop
方法;當(dāng)我們希望在作用域結(jié)束之前就強(qiáng)制釋放變量的話,我們應(yīng)該使用的是由標(biāo)準(zhǔn)庫提供的 std::mem::drop
。
如果我們像是示例 15-14 那樣嘗試調(diào)用 Drop
trait 的 drop
方法,就會(huì)得到像示例 15-15 那樣的編譯錯(cuò)誤:
文件名: src/main.rs
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:嘗試手動(dòng)調(diào)用 Drop
trait 的 drop
方法提早清理
如果嘗試編譯代碼會(huì)得到如下錯(cuò)誤:
$ 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
錯(cuò)誤信息表明不允許顯式調(diào)用 drop
。錯(cuò)誤信息使用了術(shù)語 析構(gòu)函數(shù)(destructor),這是一個(gè)清理實(shí)例的函數(shù)的通用編程概念。析構(gòu)函數(shù) 對(duì)應(yīng)創(chuàng)建實(shí)例的 構(gòu)造函數(shù)。Rust 中的 drop
函數(shù)就是這么一個(gè)析構(gòu)函數(shù)。
Rust 不允許我們顯式調(diào)用 drop
因?yàn)?Rust 仍然會(huì)在 main
的結(jié)尾對(duì)值自動(dòng)調(diào)用 drop
,這會(huì)導(dǎo)致一個(gè) double free 錯(cuò)誤,因?yàn)?Rust 會(huì)嘗試清理相同的值兩次。
因?yàn)椴荒芙卯?dāng)值離開作用域時(shí)自動(dòng)插入的 drop
,并且不能顯式調(diào)用 drop
,如果我們需要強(qiáng)制提早清理值,可以使用 std::mem::drop
函數(shù)。
std::mem::drop
函數(shù)不同于 Drop
trait 中的 drop
方法??梢酝ㄟ^傳遞希望提早強(qiáng)制丟棄的值作為參數(shù)。std::mem::drop
位于 prelude,所以我們可以修改示例 15-15 中的 main
來調(diào)用 drop
函數(shù)。如示例 15-16 所示:
文件名: src/main.rs
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: 在值離開作用域之前調(diào)用 std::mem::drop
顯式清理
運(yùn)行這段代碼會(huì)打印出如下:
$ 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.
Dropping CustomSmartPointer with data `some data`!
出現(xiàn)在 CustomSmartPointer created.
和 CustomSmartPointer dropped before the end of main.
之間,表明了 drop
方法被調(diào)用了并在此丟棄了 c
。
Drop
trait 實(shí)現(xiàn)中指定的代碼可以用于許多方面,來使得清理變得方便和安全:比如可以用其創(chuàng)建我們自己的內(nèi)存分配器!通過 Drop
trait 和 Rust 所有權(quán)系統(tǒng),你無需擔(dān)心之后的代碼清理,Rust 會(huì)自動(dòng)考慮這些問題。
我們也無需擔(dān)心意外的清理掉仍在使用的值,這會(huì)造成編譯器錯(cuò)誤:所有權(quán)系統(tǒng)確保引用總是有效的,也會(huì)確保 drop
只會(huì)在值不再被使用時(shí)被調(diào)用一次。
現(xiàn)在我們學(xué)習(xí)了 Box<T>
和一些智能指針的特性,讓我們聊聊標(biāo)準(zhǔn)庫中定義的其他幾種智能指針。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: