はじめに
「Drop トレイトを使ったクリーンアップ時のコード実行」へようこそ。この実験は、Rust Bookの一部です。LabEx で Rust のスキルを練習することができます。
この実験では、Rust のDropトレイトを調べます。これは、値がスコープ外になるときのクリーンアップアクションのカスタマイズを可能にします。主にスマートポインタで使用され、Rust によって自動的に呼び出されるdropメソッドを提供することで実装できます。これにより、明示的なクリーンアップコードが不要になります。
Drop トレイトを使ったクリーンアップ時のコード実行
スマートポインタパターンにとって重要な 2 番目のトレイトはDropで、これは値がスコープ外になろうとしているときに何が起こるかをカスタマイズできるようにします。任意の型に対してDropトレイトの実装を提供でき、そのコードを使ってファイルやネットワーク接続などのリソースを解放できます。
私たちは、Dropトレイトの機能がスマートポインタを実装する際にほとんど常に使用されるため、スマートポインタの文脈でDropを紹介しています。たとえば、Box<T>が破棄されると、ボックスが指すヒープ上の領域が解放されます。
一部の言語では、一部の型について、プログラマはそれらの型のインスタンスを使用し終えるたびに、メモリやリソースを解放するコードを呼び出さなければなりません。これには、ファイルハンドル、ソケット、ロックなどが挙げられます。もし忘れてしまうと、システムが過負荷になりクラッシュする可能性があります。Rust では、値がスコープ外になるたびに特定のコードを実行するように指定でき、コンパイラが自動的にこのコードを挿入します。その結果、特定の型のインスタンスが使用終了したプログラム内のあらゆる場所にクリーンアップコードを配置することに注意する必要がなくなります。それでもリソースが漏れることはありません!
値がスコープ外になるときに実行するコードを指定するには、Dropトレイトを実装します。Dropトレイトでは、selfへのミュータブル参照を取るdropという名前の 1 つのメソッドを実装する必要があります。Rust がdropを呼び出すタイミングを見るために、今はprintln!文でdropを実装しましょう。
リスト 15-14 には、CustomSmartPointer構造体が示されています。この構造体の唯一のカスタム機能は、インスタンスがスコープ外になるときにDropping CustomSmartPointer!と表示することで、Rust がdropメソッドを実行するタイミングを示します。
ファイル名:src/main.rs
struct CustomSmartPointer {
data: String,
}
1 impl Drop for CustomSmartPointer {
fn drop(&mut self) {
2 println!(
"Dropping CustomSmartPointer with data `{}`!",
self.data
);
}
}
fn main() {
3 let c = CustomSmartPointer {
data: String::from("my stuff"),
};
4 let d = CustomSmartPointer {
data: String::from("other stuff"),
};
5 println!("CustomSmartPointers created.");
6 }
リスト 15-14: Dropトレイトを実装したCustomSmartPointer構造体で、クリーンアップコードを配置する場所を示す
Dropトレイトはプレリュードに含まれているため、スコープに持ち込む必要はありません。私たちはCustomSmartPointerに対してDropトレイトを実装し[1]、println!を呼び出すdropメソッドの実装を提供します[2]。dropメソッドの本体は、型のインスタンスがスコープ外になるときに実行したい任意のロジックを配置する場所です。ここでは、Rust がdropを呼び出すタイミングを視覚的に示すために、いくつかのテキストを表示しています。
main関数では、[3]と[4]でCustomSmartPointerの 2 つのインスタンスを作成し、その後CustomSmartPointers createdを表示します[5]。mainの末尾[6]で、CustomSmartPointerのインスタンスはスコープ外になり、Rust はdropメソッド[2]に入れたコードを呼び出し、最終的なメッセージを表示します。明示的にdropメソッドを呼ぶ必要はありません。
このプログラムを実行すると、以下の出力が表示されます。
CustomSmartPointers created.
Dropping CustomSmartPointer with data `other stuff`!
Dropping CustomSmartPointer with data `my stuff`!
インスタンスがスコープ外になるとき、Rust は自動的に私たちのためにdropを呼び出し、指定したコードを呼び出しました。変数は作成順の逆順で破棄されるため、dはcの前に破棄されました。この例の目的は、dropメソッドがどのように機能するかを視覚的なガイドにすることです。通常は、型が実行するクリーンアップコードを指定することになります。印刷メッセージではなく、型が実行するクリーンアップコードを指定することになります。
残念ながら、自動的なdrop機能を無効にするのは簡単ではありません。dropを無効にすることは通常必要ありません。Dropトレイトの主なポイントは、自動的に処理されることです。ただし、場合によっては、値を早期にクリーンアップしたい場合があります。その 1 つの例は、ロックを管理するスマートポインタを使用する場合です。同じスコープ内の他のコードがロックを取得できるように、ロックを解放するdropメソッドを強制したい場合があります。Rust は、Dropトレイトのdropメソッドを手動で呼ぶことはできません。代わりに、値がスコープの終了前に破棄されるように強制したい場合は、標準ライブラリに提供されているstd::mem::drop関数を呼び出す必要があります。
リスト 15-14 のmain関数を変更して、Dropトレイトのdropメソッドを手動で呼び出そうとすると、コンパイラエラーが発生します。
ファイル名: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: 早期にクリーンアップするために、Dropトレイトのdropメソッドを手動で呼び出そうとする
このコードをコンパイルしようとすると、以下のエラーが表示されます。
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)`
このエラーメッセージは、明示的にdropを呼ぶことはできないことを示しています。エラーメッセージでは、インスタンスをクリーンアップする関数を一般的なプログラミング用語である「デストラクタ」と呼んでいます。「デストラクタ」は、インスタンスを作成する「コンストラクタ」に似ています。Rust のdrop関数は、特定のデストラクタです。
Rust は明示的にdropを呼び出さないようにしています。なぜなら、Rust はmainの末尾でも自動的に値に対してdropを呼び出すからです。これは、同じ値を 2 回クリーンアップしようとするため、「二重解放」エラーを引き起こします。
値がスコープ外になるときにdropの自動挿入を無効にすることはできず、明示的にdropメソッドを呼ぶこともできません。したがって、値を早期にクリーンアップする必要がある場合は、std::mem::drop関数を使用します。
std::mem::drop関数は、Dropトレイトのdropメソッドとは異なります。強制的に破棄したい値を引数として渡して呼び出します。この関数はプレリュードにあるため、リスト 15-15 のmainを変更してdrop関数を呼び出すことができます。
ファイル名: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: 値がスコープ外になる前に明示的に破棄するためにstd::mem::dropを呼び出す
このコードを実行すると、以下のように表示されます。
CustomSmartPointer created.
Dropping CustomSmartPointer with data `some data`!
CustomSmartPointer dropped before the end of main.
CustomSmartPointer created.とCustomSmartPointer dropped before the end of main.の間に、Dropping CustomSmartPointer with data some data!というテキストが表示されます。これは、その時点でcを破棄するためにdropメソッドコードが呼び出されたことを示しています。
Dropトレイトの実装で指定したコードを、クリーンアップを便利で安全にするために様々な方法で使用できます。たとえば、独自のメモリ割り当て器を作成するために使用できます!Dropトレイトと Rust の所有権システムを使うことで、クリーンアップを忘れる必要がなくなります。なぜなら、Rust が自動的に行ってくれるからです。
また、まだ使用中の値を偶発的にクリーンアップすることで引き起こされる問題も心配する必要はありません。参照が常に有効であることを保証する所有権システムは、値がもはや使用されていないときにdropが 1 回だけ呼ばれることも保証します。
これまでBox<T>とスマートポインタのいくつかの特性を調べましたので、標準ライブラリで定義されている他のいくつかのスマートポインタを見てみましょう。
まとめ
おめでとうございます!「Drop トレイトを使ったクリーンアップ時のコード実行」の実験を完了しました。LabEx でさらに多くの実験を行って、スキルを向上させることができます。