注意: 最新版のドキュメントをご覧ください。この第1版ドキュメントは古くなっており、最新情報が反映されていません。リンク先のドキュメントが現在の Rust の最新のドキュメントです。

演算子とオーバーロード

Rustは制限された形式での演算子オーバーロードを提供しており、オーバーロード可能な演算子がいくつか存在します。 型同士の間の演算子をサポートするためのトレイトが存在し、それらを実装することで演算子をオーバーロードできます。

たとえば、 + の演算子は Add トレイトでオーバーロードできます:

use std::ops::Add; #[derive(Debug)] struct Point { x: i32, y: i32, } impl Add for Point { type Output = Point; fn add(self, other: Point) -> Point { Point { x: self.x + other.x, y: self.y + other.y } } } fn main() { let p1 = Point { x: 1, y: 0 }; let p2 = Point { x: 2, y: 3 }; let p3 = p1 + p2; println!("{:?}", p3); }
use std::ops::Add;

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point { x: self.x + other.x, y: self.y + other.y }
    }
}

fn main() {
    let p1 = Point { x: 1, y: 0 };
    let p2 = Point { x: 2, y: 3 };

    let p3 = p1 + p2;

    println!("{:?}", p3);
}

main 中で、2つの Point に対して + を使えます。 これは Point に対して Add<Output=Point> を実装したためです。

同じ方法でオーバーロード可能な演算子が多数あります。 それらに対応したトレイトは std::ops モジュール内に存在します。 全てのオーバーロード可能な演算子と対応するトレイトについては std::ops のドキュメントを読んで確認して下さい。

それらのトレイトの実装は、ある一つのパターンに従います。 Add トレイトを詳しく見ていきましょう:

fn main() { mod foo { pub trait Add<RHS = Self> { type Output; fn add(self, rhs: RHS) -> Self::Output; } } }
pub trait Add<RHS = Self> {
    type Output;

    fn add(self, rhs: RHS) -> Self::Output;
}

関連する3つの型が存在します: impl Add を実装するもの、 デフォルトが SelfRHS、 そして Output です。 たとえば、式 let z = x + y においては xSelfy は RHS、 zSelf::Output 型となります。

fn main() { struct Point; use std::ops::Add; impl Add<i32> for Point { type Output = f64; fn add(self, rhs: i32) -> f64 { // // add an i32 to a Point and get an f64 // i32をPointに加算しf64を返す 1.0 } } }
impl Add<i32> for Point {
    type Output = f64;

    fn add(self, rhs: i32) -> f64 {
        // i32をPointに加算しf64を返す
    }
}

上のコードによって以下の様に書けるようになります:

fn main() { let p: Point = // ... let x: f64 = p + 2i32; }
let p: Point = // ...
let x: f64 = p + 2i32;

オペレータトレイトをジェネリック構造体で使う

オペレータトレイトがどのように定義されているかを学びましたので、トレイトについての章HasArea トレイトと Square 構造体をさらに一般的に定義できます:

use std::ops::Mul; trait HasArea<T> { fn area(&self) -> T; } struct Square<T> { x: T, y: T, side: T, } impl<T> HasArea<T> for Square<T> where T: Mul<Output=T> + Copy { fn area(&self) -> T { self.side * self.side } } fn main() { let s = Square { x: 0.0f64, y: 0.0f64, side: 12.0f64, }; println!("Area of s: {}", s.area()); }
use std::ops::Mul;

trait HasArea<T> {
    fn area(&self) -> T;
}

struct Square<T> {
    x: T,
    y: T,
    side: T,
}

impl<T> HasArea<T> for Square<T>
        where T: Mul<Output=T> + Copy {
    fn area(&self) -> T {
        self.side * self.side
    }
}

fn main() {
    let s = Square {
        x: 0.0f64,
        y: 0.0f64,
        side: 12.0f64,
    };

    println!("Area of s: {}", s.area());
}

HasAreaSquare について、型パラメータ T を宣言し f64 で置換しました。 impl はさらに関連する修正を必要とします:

fn main() { impl<T> HasArea<T> for Square<T> where T: Mul<Output=T> + Copy { ... } }
impl<T> HasArea<T> for Square<T>
        where T: Mul<Output=T> + Copy { ... }

area メソッドは辺を掛けることが可能なことを必要としています。 そのため型 Tstd::ops::Mul を実装していなければならないと宣言しています。 上で説明した Add と同様に、MulOutput パラメータを取ります: 数値を掛け算した時に型が変わらないことを知っていますので、 OutputT と設定します。 また T は、Rustが self.side を返り値にムーブするのを試みないようにコピーをサポートしている必要があります。