項目11:RAIIパターンにはDropトレイトを実装しよう
RAII はメモリ以外のリソース管理にも有用
「解放しなければならない何らかのリソースを保持する型には、Drop を実装しよう」
e.g. 使用例
これを正しく解放しないと、システムのリソースを保持したままになり、最終的にはプロセスごとのファイルディスクリプタの上限に達する
メモリ同期機構については標準ライブラリに用意されている
e.g. Mutex / RwLock
しかし、他のリソース(ファイルロックやデータベースロック)に対しても同様の カプセル化 が必要な場合がある 生メモリにアクセスするケース
低レイヤのメモリ管理を行う unsafe な型(e.g. FFI など) Mutex のロックを保持している間は、MutexGuard を介して保護されたデータにアクセスする
code:rs
use std::sync::Mutex;
struct ThreadSafeInt {
value: Mutex<i32>,
}
impl ThreadSafeInt {
fn new(value: i32) -> Self {
Self { value: Mutex::new(value) }
}
fn add(&self, delta: i32) {
let mut v = self.value.lock().unwrap();
*v += delta;
}
}
code:rs
impl TreadSafeInt {
fn add_with_extras(&self, delta: i32) {
{
let mut v = self.value.lock().unwrap();
*v += delta;
}
// ロックを必要としないコードが続く...
}
}
RAII パターンの実装方法
必須メソッドは drop のみ
コンパイラ はメモリを解放する直前に、このメソッドを呼び出す warning.icon
このメソッドは明示的に呼び出すことができない
code:rs
x.drop();
理由
シグネチャは drop(&mut self) であり、移動した値を受け取るのではなく可変参照を受け取る
そのため、drop メソッドが明示的に呼び出せると、内部状態やリソースが解放されているにも関わらず、呼び出した後でも使用できてしまう
code:rs
{
x.drop();
x.0 += 1;
}
この関数では、引数をムーブで受け取るようになっている
pub fn drop<T>(_x: T)
また、実装は空のボディである
code:rs
pub fn drop<T>(_x: T) {}
したがって、ムーブした値はこの関数の波括弧が閉じたタイミングでドロップされる
戻り値の型は無い
そのため、リソースの解放に失敗する可能性がある 場合、Result 型を返す release メソッドを作成し、失敗を検出できるようにする必要がある
実装例
code:rs
struct MyStruct(i32);
impl Drop for MyStruct {
fn drop(&mut self) {
println!("Dropping {self:?}");
}
}