【Rust】異なるミュータビリティの参照を返す関数

※Rustのバージョンは1.33.0 (stable)を使用しています。

Rustでは、`&S`と`&mut S`は別の型扱いになるため、似たような処理をする場合でも、戻り値が`&S`の場合と`&mut S`の場合では別の関数を書かなければなりません。
この2つの関数を*ゼロコスト抽象化*で共通化できないかというのが、この記事の主題です。

#こんな関数を共通化してみる

```rust
/// テスト用構造体
struct MyStruct<V> {
    pivot: i32,
    left: V,
    right: V
}

impl<V> MyStruct<V> {

    /// key >= pivotならright, key < pivotならleftを返す
    /// immutable版
    fn left_or_right_ref(&self, key:i32) -> &V {
        if key >= self.pivot {
            &self.right
        } else {
            &self.left
        }
    }

    /// key >= pivotならright, key < pivotならleftを返す
    /// mutable版
    fn left_or_right_mut(&mut self, key:i32) -> &mut V {
        if key >= self.pivot {
            &mut self.right
        } else {
            &mut self.left
        }
    }

}
```

immutable版とmutable版では中身はほぼ同じですが、戻り値が別の型となるため、通常は2つのメソッドを用意する必要があります。この2つメソッドの内部処理を共通化してみます。

#完成形
まずは、完成した形を載せます。詳細は後述。

```rust:完成形
use std::borrow::Borrow;

/// テスト用構造体
struct MyStruct<V> {
    pivot: i32,
    left: V,
    right: V
}

/// テスト用構造体のプロパティ用トレイト
trait MyStructRef<V> : Borrow<MyStruct<V>> {

    /// pivotの型
    type PivotValue;
    /// left, rightの型
    type Value;

    fn fields(self) -> (Self::PivotValue, Self::Value, Self::Value);

}

/// &MyStruct<V>のプロパティ用トレイト実装
impl<'a, V> MyStructRef<V> for &'a MyStruct<V> {
    type PivotValue = &'a i32;
    type Value = &'a V;

    fn fields(self) -> (Self::PivotValue, Self::Value, Self::Value) {
        (&self.pivot, &self.left, &self.right) 
    }
}

/// &mut MyStruct<V>のプロパティ用トレイト実装
impl<'a, V> MyStructRef<V> for &'a mut MyStruct<V> {

    type PivotValue = &'a mut i32;
    type Value = &'a mut V;

    fn fields(self) -> (Self::PivotValue, Self::Value, Self::Value) {
        (&mut self.pivot, &mut self.left, &mut self.right) 
    }

}

impl<V> MyStruct<V> {

    /// key >= pivotならright, key < pivotならleftを返す
    fn left_or_right<T>(my_refmut:T, key:i32) -> T::Value
    where T: MyStructRef<V> {
        // &mut MyStruct<V>と&MyStruct<V>を&MyStruct<V>に揃える
        // &MyStruct<V>で型を確定すると、フィールドにアクセスできる
        let my_ref = my_refmut.borrow();
        if key >= my_ref.pivot {
            let (_, _, right) = my_refmut.fields();
            // T = &mut MyStruct<V>なら&mut V
            // T = &MyStruct<V>なら&V
            right
        } else {
            let (_, left, _) = my_refmut.fields();
            // T = &mut MyStruct<V>なら&mut V
            // T = &MyStruct<V>なら&V
            left
        }
    }

    fn left_or_right_ref(&self, key:i32) -> &V {
        Self::left_or_right(self, key)
    }

    fn left_or_right_mut(&mut self, key:i32) -> &mut V {
        Self::left_or_right(self, key)
    }

}
```

#順を追ってやってみる
##方針
まずは、2つのメソッドから呼び出される、関連関数（`self`を引数に取らない関数）の定義を考えます。

```rust
fn left_or_right<T>(my_refmut:T, key:i32) -> &V か &mut V
where T: &MyStruct<V>と&mut MyStruct<V>で実装のあるトレイト
```
こんなイメージの関数があれば、共通化できそうです。
`&S`と`&mut S`で実装のあるトレイトは、標準ライブラリリファレンスの[reference](https://doc.rust-lang.org/std/primitive.reference.html "reference - Rust")に記述があります。その中では[std::borrow::Borrow](https://doc.rust-lang.org/std/borrow/trait.Borrow.html "std::borrow::Borrow - Rust")が使えそうです。が、`Borrow`をトレイト境界にしただけでは、`MyStruct`のフィールドをイミュータブルでしか扱えず、ミュータブルで戻す処理が書けません。

```rust
fn left_or_right<T>(my_refmut:T, key:i32) -> &V か &mut V
where T: Borrow<MyStruct<V>> {
    // &mut MyStruct<V>と&MyStruct<V>を&MyStruct<V>に揃える
    // &MyStruct<V>で型を確定すると、フィールドにアクセスできる
    let my_ref = my_refmut.borrow();
    if key >= my_ref.pivot {
        // my_refmutはジェネリック型T
        // ジェネリック型からはフィールドが見えないので下記の処理はエラー
        ☓ my_refmut.right
        
        // my_refは&MyStruct<V>
        // &my_ref.rightは可能だが、&mut my_ref.rightはエラー
        ☓ &my_ref.right
    } else {
        ☓ my_refmut.left
        ☓ &my_ref.left
    }
}
```
というわけで、標準ライブラリだけでの実装は諦め、トレイトを自作します。

##トレイトの作成
上記の方針でだめだったポイントは、「**ミュータビリティを保持したままフィールドにアクセスできない**」点なので、フィールドにアクセスできるように、フィールドを返すトレイトを定義します。

```rust
/// テスト用構造体のプロパティ用トレイト
trait MyStructRef<V> : Borrow<MyStruct<V>> {
    /// pivotの型
    type PivotValue;
    /// left, rightの型
    type Value;

    fn fields(self) -> (Self::PivotValue, Self::Value, Self::Value);
}
```
`Borrow<MyStruct<V>>`を継承してますが、必ず必要というわけではありません。`&MyStruct`として扱いたい場面は多いと思うので、付けておいたほうが便利かと思います。
各フィールドの戻り値は、`&V`か`&mut V`にするので、ジェネリック（関連型）でそれぞれ定義しておきます。
今回は関連型にトレイト境界は使ってませんが、もし戻り値に対して必要な操作がある場合は、

```rust
type Value: Borrow<V>;
```
のように制約を入れると、トレイトのメソッドが使えるようになります。
また、`fields`は所有権を要求している点に注意してください。このメソッドを使用後は、元の構造体の参照にはアクセスできなくなります。(所有権を要求しない方法は、unsafeを使用しなければないと思っていますが…どうでしょう。)
では、`&MyStruct<V>`と`&mut MyStruct<V>`に対して、このトレイトを実装します。

```rust
/// &MyStruct<V>のプロパティ用トレイト実装
impl<'a, V> MyStructRef<V> for &'a MyStruct<V> {
    type PivotValue = &'a i32;
    type Value = &'a V;

    fn fields(self) -> (Self::PivotValue, Self::Value, Self::Value) {
        (&self.pivot, &self.left, &self.right) 
    }
}

/// &mut MyStruct<V>のプロパティ用トレイト実装
impl<'a, V> MyStructRef<V> for &'a mut MyStruct<V> {

    type PivotValue = &'a mut i32;
    type Value = &'a mut V;

    fn fields(self) -> (Self::PivotValue, Self::Value, Self::Value) {
        (&mut self.pivot, &mut self.left, &mut self.right) 
    }

}
```

##共通関数の実装
あとは、先程の方針通りに共通関数を実装します。

```rust
impl<V> MyStruct<V> {

    /// key >= pivotならright, key < pivotならleftを返す
    fn left_or_right<T>(my_refmut:T, key:i32) -> T::Value
    where T: MyStructRef<V> {
        // &mut MyStruct<V>と&MyStruct<V>を&MyStruct<V>に揃える
        // &MyStruct<V>で型を確定すると、フィールドにアクセスできる
        let my_ref = my_refmut.borrow();
        if key >= my_ref.pivot {
            let (_, _, right) = my_refmut.fields();
            // T = &mut MyStruct<V>なら&mut V
            // T = &MyStruct<V>なら&V
            right
        } else {
            let (_, left, _) = my_refmut.fields();
            // T = &mut MyStruct<V>なら&mut V
            // T = &MyStruct<V>なら&V
            left
        }
    }

    fn left_or_right_ref(&self, key:i32) -> &V {
        Self::left_or_right(self, key)
    }

    fn left_or_right_mut(&mut self, key:i32) -> &mut V {
        Self::left_or_right(self, key)
    }

}
```
戻り値が`&V`か`&mut V`となるのは、`MyStructRef`トレイトの関連型`Value`なので、戻り値は`T::Value`と指定します。この型はコンパイル時に必ず一意に決まります。
また、前述の通り、`my_refmut.fields()`は所有権を奪っているため、呼び出した後は`my_refmut`や`my_ref`は使用できません。
以上で実装が完了しました。

##試しになんか動かしてみる

```rust:検証コード
fn main() {
    let mut a = MyStruct { pivot: 0, left:"left".to_string(), right:"right".to_string() };

    let rmut = a.left_or_right_mut(0);
    println!("{:?}", rmut);

    *rmut = "俺様参上!!".to_string();

    let rref = a.left_or_right_ref(0);
    println!("{:?}", rref);
}
```

```terminal:実行結果
"right"
"俺様参上!!"
```

#最後に
専用のフィールドを返すトレイトを実装するのは、割とめんどくさい。`&S`と`&mut S`の`enum`を作って場合分けでいいんじゃないかと思ってしまいます。
もっといい方法があれば、コメントにてご教示願います。