DropとGuard, MaybeUninit

# Dropとは

RustはRAIIの強制と所有権を用いてリソースの管理をしています。
このリソース管理の一端を担っている仕組みとして`Drop`があります。
コンパイラは、所有権が消えたと判断した場所に自動的にリソースの解放するコードを挿入してくれます。
例えばstructならフィールドを上から解放していきます。


```rust
{
    let x = Box::new(5);
    println!("{x}");
    //ここでxは開放される
}

```

ただ解放する際に追加で処理したいときがあります。
ポインタで持っているデータを解放したりとか、解放したことをどこかに通知したいなどです。そういう時には`Drop`を使います。
たとえば[`Vec`](https://doc.rust-lang.org/src/alloc/vec/mod.rs.html#2914)はアロケートされているデータをすべて開放したり、[`Arc`](https://doc.rust-lang.org/src/alloc/sync.rs.html#1636)では参照カウンタを減らし、参照カウンタが0になったらリソースを開放するということを行っています。

まあ他の言語でいうデストラクタですね。

Rustでライブラリを書いたりしてリソースの管理をしない限り触る必要はあんまりなく意識されないtraitです。
ただ少しパフォーマンスを意識したりしてunsafe Rustに入り始めたときには必要になってくるものです。その際の一つのパターンを紹介します。

# Guard

Guardといってもそういったtraitがあるわけでもなく、そういった名前のstructで実装することが多いからこの名前でとってきました。

このGuardは何のために用いるかというと、リソースの初期化中に初期化に失敗したときにリソースを安全に解放するためです。
Rustは所有権を解放したときにdropされるはずで、panicしたとしても実行されるはずですが、どうしてこのGuardが必要になるのでしょうか？
それはdropされない型があるからです。
ポインタ型はそのポインタが指す相手は解放しませんし、
またunion型もそのデータは解放されません（というよりDropする型は入れれません）。
そしてunionの中で最も使われるのが`MaybeUninit`です。

## MaybeUninit

例えば初期化したいとしましょう。
メモリ領域を確保したとして、Rustではその領域のデータが型としての制約に従っているようにしないといけません。
(`bool`では、`0x01`と`0x00`だけのバイトである、`str`はUtf-8に従っているなど)。
しかし、Arrayなど生成時にはすべてのデータが規約に従っている状態で作れない場合があります。[^1]
この時に`MaybeUninit`を用いることでまだ未初期化であると宣言することができます。
そして初期化が終わったときに通常の型に戻せます。(もちろんunsafeです、コンパイラが初期化したかを保証できないので。)

```rust
let mut uninit = MaybeUninit::uninit();
uninit.write(5_i32);
let init = unsafe { uninit.assume_init() };
```

上のように`MaybeUninit`で初期化した後に`assume_init`で通常の型に戻すのですが、戻す前にpanicしたらどうなるでしょうか？

```rust
let init = "bbbb".to_owned();
let mut uninit = MaybeUninit::uninit();
uninit.write("aaaa".to_owned());

panic!();

// initのStringは解放される。
//uninitに残ったStringは解放されない！

```
dropされない型なので確保されたリソースが解放されません。メモリリークになります！
panicしないときでも例えば`Result`を返す型で早くに返したいけどしっかりとリソースの解放をしたいときには困ります。

# 再びGuardに戻って

このようにpanicしたときにメモリリークを防ぐのはGuardパターンです。
https://doc.rust-lang.org/src/core/array/mod.rs.html#804
などのような関数の内側で実装されています。

上の例だとGuardは以下のように実装されています。

```rust
struct Guard<'a, T, const N: usize> {
    array_mut: &'a mut [MaybeUninit<T>; N],
    initialized: usize,
}

impl<T, const N: usize> Drop for Guard<'_, T, N> {
    fn drop(&mut self) {
        debug_assert!(self.initialized <= N);

        // SAFETY: this slice will contain only initialized objects.
        unsafe {
            crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(
                &mut self.array_mut.get_unchecked_mut(..self.initialized),
            ));
        }
    }
}
```

これはGuard型が不意に`Drop`するときにそれまで初期化していた範囲をしっかりと解放するように用いられています。

初期化時の変更をしていくたびに`initialized`を更新していきます。
そして最終的には中の参照先のリソースを`assume_init`することで、リソースが初期化済みであることをしめして、Guard型を`forget`して解放が不要であることを知らせます。(これを忘れると誤って解放されることになります。)

```rust
let mut array = MaybeUninit::uninit_array::<N>();
let mut guard = Guard { array_mut: &mut array, initialized: 0 };

//arrayを初期化する
//ここで落ちるとGuardがdropしてリソースが解放される。

mem::forget(guard);// ここが重要　forgetをするとDropが呼ばれなくなる。
let output = unsafe { MaybeUninit::array_assume_init(array) };
```

この`forget`はDropの対象から外すことになります。
ただこの`forget`はいろいろ危険なのにunsafeでないので気を付けてください…。
基本的には`forget`を使わずに`ManuallyDrop`を使うといいとされています。
まあ`forget`の中身は`let _ = ManuallyDrop::new(t);`なのですが。

# 終わりに

そんな感じで`Drop`と`MaybeUninit`を紹介する傍らでGuardと言われるパターンを見ていきました。Rustはunsafeでもなるべく安全に書けるようになっています。まあunsafeは極力触らない方がいいのですが。


[^1]: 実際はstdには安全なラッパーが用意されていて、safe Rustの範囲内で実装できることが増えてきた。1.63 からの[std::array::from_fn](https://doc.rust-lang.org/std/array/fn.from_fn.html)など便利になってきた。