Drop
トレイトで片付け時にコードを走らせる
スマートポインタパターンにとって重要な2番目のトレイトは、Drop
であり、
これのおかげで値がスコープを抜けそうになった時に起こることをカスタマイズできます。
どんな型に対してもDrop
トレイトの実装を提供することができ、指定したコードは、
ファイルやネットワーク接続などのリソースを解放するのに活用できます。
Drop
をスマートポインタの文脈で導入しています。Drop
トレイトの機能は、ほぼ常にスマートポインタを実装する時に使われるからです。
例えば、Box<T>
はDrop
をカスタマイズしてボックスが指しているヒープの領域を解放しています。
ある言語では、プログラマがスマートポインタのインスタンスを使い終わる度にメモリやリソースを解放するコードを呼ばなければなりません。 忘れてしまったら、システムは詰め込みすぎになりクラッシュする可能性があります。Rustでは、 値がスコープを抜ける度に特定のコードが走るよう指定でき、コンパイラはこのコードを自動的に挿入します。 結果として、特定の型のインスタンスを使い終わったプログラムの箇所全部にクリーンアップコードを配置するのに配慮する必要はありません。 それでもリソースをリークすることはありません。
Drop
トレイトを実装することで値がスコープを抜けた時に走るコードを指定してください。
Drop
トレイトは、self
への可変参照を取るdrop
という1つのメソッドを実装する必要があります。
いつRustがdrop
を呼ぶのか確認するために、今はprintln!
文のあるdrop
を実装しましょう。
リスト15-14は、唯一の独自の機能が、インスタンスがスコープを抜ける時にDropping CustomSmartPointer!
と出力するだけの、
CustomSmartPointer
構造体です。この例は、コンパイラがいつdrop
関数を走らせるかをデモしています。
ファイル名: src/main.rs
struct CustomSmartPointer { data: String, } impl Drop for CustomSmartPointer { fn drop(&mut self) { // CustomSmartPointerをデータ`{}`とともにドロップするよ 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."); // CustomSmartPointerが生成された }
Drop
トレイトは、初期化処理に含まれるので、インポートする必要はありません。
CustomSmartPointer
にDrop
トレイトを実装し、println!
を呼び出すdrop
メソッドの実装を提供しています。
drop
関数の本体は、自分の型のインスタンスがスコープを抜ける時に走らせたいあらゆるロジックを配置する場所です。
ここで何らかのテキストを出力し、コンパイラがいつdrop
を呼ぶのかデモしています。
main
で、CustomSmartPointer
のインスタンスを2つ作り、それからCustomSmartPointers created.
と出力しています。
main
の最後で、CustomSmartPointer
のインスタンスはスコープを抜け、コンパイラは最後のメッセージを出力しながら、
drop
メソッドに置いたコードを呼び出します。drop
メソッドを明示的に呼び出す必要はなかったことに注意してください。
このプログラムを実行すると、以下のような出力が出ます:
CustomSmartPointers created.
Dropping CustomSmartPointer with data `other stuff`!
Dropping CustomSmartPointer with data `my stuff`!
インスタンスがスコープを抜けた時に指定したコードを呼び出しながらコンパイラは、drop
を自動的に呼び出してくれました。
変数は、生成されたのと逆の順序でドロップされるので、d
はc
より先にドロップされました。
この例は、drop
メソッドの動き方を見た目で案内するだけですが、通常は、メッセージ出力ではなく、
自分の型が走らせる必要のあるクリーンアップコードを指定するでしょう。
std::mem::drop
で早期に値をドロップする
残念ながら、自動的なdrop
機能を無効化することは、単純ではありません。通常、drop
を無効化する必要はありません;
Drop
トレイトの最重要な要点は、自動的に考慮されることです。ですが、時として、値を早期に片付けたくなる可能性があります。
一例は、ロックを管理するスマートポインタを使用する時です: 同じスコープの他のコードがロックを獲得できるように、
ロックを解放するdrop
メソッドを強制的に走らせたくなる可能性があります。Rustは、
Drop
トレイトのdrop
メソッドを手動で呼ばせてくれません; スコープが終わる前に値を強制的にドロップさせたいなら、
代わりに標準ライブラリが提供するstd::mem::drop
関数を呼ばなければなりません。
リスト15-14のmain
関数を変更して手動でDrop
トレイトのdrop
メソッドを呼び出そうとしたら、
コンパイルエラーになるでしょう。リスト15-15のようにですね:
ファイル名: src/main.rs
fn main() {
let c = CustomSmartPointer { data: String::from("some data") };
println!("CustomSmartPointer created.");
c.drop();
// mainの終端の前にCustomSmartPointerがドロップされた
println!("CustomSmartPointer dropped before the end of main.");
}
このコードをコンパイルしてみようとすると、こんなエラーが出ます:
error[E0040]: explicit use of destructor method
(エラー: デストラクタメソッドを明示的に使用しています)
--> src/main.rs:14:7
|
14 | c.drop();
| ^^^^ explicit destructor calls not allowed
明示的にdrop
を呼び出すことは許されていないことをこのエラーメッセージは述べています。
エラーメッセージはデストラクタという専門用語を使っていて、これは、
インスタンスを片付ける関数の一般的なプログラミング専門用語です。デストラクタは、
コンストラクタに類似していて、これはインスタンスを生成します。Rustのdrop
関数は、
1種の特定のデストラクタです。
コンパイラはそれでも、main
の終端で値に対して自動的にdrop
を呼び出すので、drop
を明示的に呼ばせてくれません。
コンパイラが2回同じ値を片付けようとするので、これは二重解放エラーになるでしょう。
値がスコープを抜けるときにdrop
が自動的に挿入されるのを無効化できず、drop
メソッドを明示的に呼ぶこともできません。
よって、値を早期に片付けさせる必要があるなら、std::mem::drop
関数を使用できます。
std::mem::drop
関数は、Drop
トレイトのdrop
メソッドとは異なります。
早期に強制的にドロップさせたい値を引数で渡すことで呼びます。この関数は初期化処理に含まれているので、
リスト15-15のmain
を変更してdrop
関数を呼び出せます。リスト15-16のようにですね:
ファイル名: src/main.rs
# struct CustomSmartPointer { # data: String, # } # # impl Drop for CustomSmartPointer { # fn drop(&mut self) { # println!("Dropping CustomSmartPointer!"); # } # } # fn main() { let c = CustomSmartPointer { data: String::from("some data") }; println!("CustomSmartPointer created."); drop(c); // CustomSmartPointerはmainが終わる前にドロップされた println!("CustomSmartPointer dropped before the end of main."); }
このコードを実行すると、以下のように出力されます:
CustomSmartPointer created.
Dropping CustomSmartPointer with data `some data`!
CustomSmartPointer dropped before the end of main.
Dropping CustomSmartPointer with data `some data`!
というテキストが、
CustomSmartPointer created.
とCustomSmartPointer dropped before the end of main.
テキストの間に出力されるので、
drop
メソッドのコードがその時点で呼び出されてc
をドロップしたことを示しています。
Drop
トレイト実装で指定されたコードをいろんな方法で使用し、片付けを便利で安全にすることができます:
例を挙げれば、これを使用して独自のメモリアロケータを作ることもできるでしょう!Drop
トレイトとRustの所有権システムがあれば、
コンパイラが自動的に行うので、片付けを覚えておく必要はなくなります。
まだ使用中の値を間違って片付けてしまうことに起因する問題を心配する必要もなくて済みます:
参照が常に有効であると確認してくれる所有権システムが、値が最早使用されなくなった時にdrop
が1回だけ呼ばれることを保証してくれるのです。
これでBox<T>
とスマートポインタの特徴の一部を調査したので、標準ライブラリに定義されている他のスマートポインタをいくつか見ましょう。