Rust のギモン その 2

前回、Point3D と Vector3D を実装するにあたって演算子オーバーロードを使用したのだけれど、std::ops::Add 等の trait で実装すべきメソッドは

fn add(self, rhs: Rhs) -> Self::Output

とあるように selfrhs も move してしまう。なので、そのまま Vector3D に対して実装すると、

let a: = Vector3D::new(1.0, 2.0, 3.0);
let b: = Vector3D::new(4.0, 5.0, 6.0);
let c = a + b;

とした場合に ab は move out してしまい、以降使用できなくなる。実際のところ、この計算では a, b のどちらも変更しないから、後でまた a, b を使っても問題ないし、使いたくなることもあり得る。なので代わりに &Vector3D に対して Add trait を実装した。

impl Add<&Vector3D> for &Vector3D {
    type Output = Vector3D;

    fn add(self, v: &Vector3D) -> Self::Output {
        Vector3D::new(self.x + v.x, self.y + v.y, self.z + v.z)
    }
}

これなら

let a: = Vector3D::new(1.0, 2.0, 3.0);
let b: = Vector3D::new(4.0, 5.0, 6.0);
let c = &a + &b;

とすることにより c を得てからも a, b 双方とも利用できる。ただ、これだと毎回 & を付けなければならないのが面倒だし、何より 3 つ以上 add したいなら

&(&a + &b) + &c;

のように書かなければならず、可読性が著しく低下する。これを回避しようとすると、型 T に対して

  • &T + &T (使用例: &a + &b)
  • &T + T (使用例: &a + (&b + &c))
  • T + &T (使用例: (&a + &b) + &c)
  • T + T (使用例: (&a + &b) + (&c + &d))

の 4 つの add を実装しなければならない。これはこれで面倒な上、誤って move out させてしまう可能性も生じる。

何か良い方法はないのだろうか。Copy を derive する以外で。

f:id:mtXTJocj:20190728233456p:plain