Rustだからこそできる安全な一時変更パターン

# 導入

オブジェクトを部分的に書き換えて何らかの処理をした後、元に戻したいことはありませんか？
これはミスが起きがちな処理ですがRustの機能を使うとミスを避けられることを紹介します。

例えば一時的に`Vec`にある値をプッシュしたいが、プッシュ前の`Vec`も後で必要になるという場合を考えてみましょう。
もちろん`Vec`をクローンしてからプッシュしても実現できますが嫌ですよね。
クローンを回避する場合次のようなコードになると思います。
(書き換え前と後が同時に必要になる場合そうはいかないのですが永続データ構造を使うとコストのかかるクローンを避けられます。Rustなら[im](https://crates.io/crates/im)が人気があるようです)

```rust
/// 内部での一時的な変更のためにミュータブルな参照を要求しますが、関数の実行後vは変更されていません。
fn hoge(v: &mut Vec<u32>) -> i32 {
    let mut value = ...  // プッシュする値を計算する
    v.push(value);

    // 変更後のVecをチェックした結果、計算をする必要がないならリターン
    if !need_calculation(&v) {
        return 0; // 変更を戻し忘れている
    }

    // 変更後のVecを使って計算する
    let n = calculate(&v);

    // 元に戻す
    v.pop();

    n
}
```

`need_calculation`の結果が`false`のとき元に戻すのを忘れています。
このように必ず最後になにかするというのはミスが起きがちです。
しかし上で述べたとおりRustに備わるRAIIと所有権を利用することでそのようなバグが起きないようにできます。

# 実際にやってみる

## 型の作成

プッシュ後の`Vec`への参照を持つ型を作ります。

```rust
pub struct Pushed<'a, T> {
    /// 値をプッシュされたVec
    inner: &'a mut Vec<T>,
}

impl<'a, T> Pushed<'a, T> {
    pub fn onto(target: &'a mut Vec<T>, value: T) -> Pushed<'a, T> {
        target.push(value);
        Pushed { inner: target }
    }
}
```

## [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html)トレイト

`Drop`を実装する型はスコープから外れると自動的に`Drop::drop`が呼び出されます。(この仕組みをRAIIと呼びます)
そこで、この型に変更を元に戻す処理を`Drop::drop`として実装してやれば復元漏れバグをなくすことができます。

```rust
impl<'a, T> Drop for Pushed<'a, T> {
    fn drop(&mut self) {
        self.inner.pop();
    }
}
```

## [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html)

次に内部の変更後の`Vec`の参照を得るメソッドを追加します。
まずはイミュータブルな参照を得ることを考えることにします。
単に`as_vec`というメソッドを追加してもいいのですが、`Deref`を実装するとより使いやすくなります。
`Deref`についての説明は「[TRPL 1st ed, `Deref` による型強制](https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/deref-coercions.html)」がわかりやすいと思います。

また、イミュータブルな場合`Vec`よりもスライスを返したほうがいいかもしれませんがこの記事では考えないことにします。

```rust
use std::ops::Deref;

impl<'a, T> Pushed<'a, T> {
    pub fn as_vec(&self) -> &Vec<T> {
        &self.inner
    }
}

impl<'a, T> Deref for Pushed<'a, T> {
    type Target = Vec<T>;

    fn deref(&self) -> &Vec<T> {
        &self.inner
    }
}
```

これで目的の型が完成しました。

# 使ってみる

実際に使ってみると次のようになります。

```rust
fn main() {
    let mut v = vec![0usize, 1];
    println!("{:?}", &v);  // [0, 1]
    {
        let p = Pushed::onto(&mut v, 100);
        println!("{:?}", &*p);  // [0, 1, 100]
    }
    println!("{:?}", &v);  // [0, 1]

    println!("{:?}", &*Pushed::onto(&mut v, 100));  // [0, 1, 100]
    println!("{:?}", &v);  // [0, 1]
}
```

きちんと変更が元に戻っていますね。

## 所有権

まだ`Pushed`がスコープに存在するときに (つまりまだスライスが変更後の状態のときに)、誤って変数`v`を通じて変更前のスライスにアクセスしようとするとコンパイルエラーになります。

```rust
fn main() {
    let mut v = vec![0usize, 1];
    let p = Pushed::onto(&mut v, 100);
    println!("{:?}", &v);  // コンパイルエラー
}
```

`Pushed`が`v`へのミュータブルな参照を持っているために`v`を借用できないからです。

このようにうまくRustの機能を使うとバグが起きにくい設計をすることができるのです。

# 使い勝手の向上 (蛇足)

## `revert`メソッド

明示的に変更を元に戻すためのメソッドを作っておくと、わざわざスコープを作って元に戻す必要がなくなります。

```rust
impl<'a, T> Pushed<'a, T> {
    fn revert(self) {}
}
```

実装は空ですが、引数が`self`で所有権を奪ってスコープを抜けているので自動的に`Drop::drop`が呼び出されます。
これは[`mem::drop`](https://doc.rust-lang.org/std/mem/fn.drop.html)のメソッド版です。

使ってみると以下のようになります。

```rust
fn main() {
    let mut v = vec![0usize, 1];
    println!("{:?}", &v);  // [0, 1]
    let p = Pushed::onto(&mut v, 100);
    println!("{:?}", &*p);  // [0, 1, 100]
    p.revert();
    println!("{:?}", &v);  // [0, 1]
}
```

注意点として、これは直接`Drop::drop`を呼び出すこととは異なります。
`Drop::drop`は引数が`&mut self`で所有権を奪いませんので、直接呼び出すと、スコープを抜けたときと二重に`Drop::drop`を呼び出すことになり、メモリの二重解放などの恐れがあります。
実際`Drop::drop`を明示的に呼び出すとコンパイルエラーになるようです。(参照: [std::ops::Drop::drop](https://doc.rust-lang.org/std/ops/trait.Drop.html#tymethod.drop))

## ミュータブルな参照

複数回の変更をするためには`Pushed`からさらにミュータブルな参照を得る必要があります。
安直に`&mut Vec<T>`への`DerefMut`を実装すると、`Vec`へのさらなるプッシュやポップがあった場合元の値と異なる値をポップしてしまいます。
そこで、複数回の変更がしたい場合は`Pushed`を引数に取り`Pushed`を返すメソッドを作成すると間違った使い方ができなくなります。

```rust
impl<'a, T> Pushed<'a, T> {
    fn onto_pushed(target: &'a mut Pushed<'b, T>, value: T) -> Pushed<'a, T> {
        Pushed::onto(&mut target.inner, value)
   }
}
```

これならばさらなる変更があったとしても、それらは先にドロップされて自分がドロップされるタイミングでは元に戻っているのでうまく動作します。

あり得る変更が他にもあるのならトレイトを使って抽象化すると他の変更と組み合わせられます。

```rust
/// 一時的な借用のトレイト.
pub trait BorrowTmp<Borrowed> {
    /// 一時的な書き換えを許されたミュータブルな参照を返す.
    ///
    /// # Safety
    ///
    /// このメソッドが返す参照の生存期間が終わるとき, 参照先は参照を得たときと同じ状態になっていなければなりません.
    unsafe fn borrow_tmp(&mut self) -> &mut Borrowed;
}

impl<T> BorrowTmp<T> for T {
    unsafe fn borrow_tmp(&mut self) -> &mut T {
        self
    }
}

impl<'a, T> BorrowTmp<Vec<T>> for Pushed<'a, T> {
    unsafe fn borrow_tmp(&mut self) -> &mut Vec<T> {
        &mut self.inner
    }
}

impl<'a, T> Pushed<'a, T> {
    pub fn onto<V>(target: &'a mut V, value: T) -> Pushed<'a, T> where V: BorrowTmp<Vec<T>> {
        unsafe {
            let inner = target.borrow_tmp();
            inner.push(value);
            Pushed { inner }
        }
    }
}
```

他にも拡張トレイトなどで使い勝手を向上できるかもしれません。