特性を使用して共有動作を定義する

完了

特性とは、型のグループで実装することができる共通のインターフェイスです。 Rust の標準ライブラリには、次のような便利な特性が多数用意されています。

  • ソースからバイトを読み取ることができる値に対する io::Read
  • バイトを書き込むことができる値に対する io::Write
  • 書式指定子 "{:?}" を使用してコンソールに出力できる値に対する Debug
  • メモリ内に明示的に複製できる値に対する Clone
  • String に変換できる値に対する ToString
  • 数値におけるゼロ、ベクターにおける空、String における “” など、適切な既定値を持つ型に対する Default
  • 値のシーケンスを生成できる型に対する Iterator

各特性定義は、不明な型について定義されたメソッドのコレクションであり、通常は、その実装元が実行できる機能または動作を表します。

"2 次元領域がある" という概念を表すために、次の特性を定義できます。

trait Area {
    fn area(&self) -> f64;
}

ここでは、trait キーワードと、その後に特性の名前 (この場合は Area) を使用して特性を宣言します。

中かっこ内で、この特性を実装する型の動作を記述するメソッド シグネチャを宣言します。この例では、関数シグネチャ fn area(&self) -> f64 です。 次に、この特性を実装する各型ではその独自のカスタム動作をメソッドの本体に指定する必要があることがコンパイラによって確認されます。

ここで、特性 Area を実装する新しい型をいくつか作成してみましょう。

struct Circle {
    radius: f64,
}

struct Rectangle {
    width: f64,
    height: f64,
}

impl Area for Circle {
    fn area(&self) -> f64 {
        use std::f64::consts::PI;
        PI * self.radius.powf(2.0)
    }
}

impl Area for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

型の特性を実装するには、キーワード impl Trait for Type を使用します。ここで、Trait は実装される特性の名前であり、Type は実装元の構造体または列挙型の名前です。

impl ブロック内には、特性の定義で必要とされたメソッド シグネチャを配置し、特性のメソッドで実行する特定の型を対象にした特定の動作を含むメソッド本体を入力します。

指定された特性が型によって実装されると、そのコントラクトは確実に維持されます。 特性を実装したら、通常のメソッドを呼び出すのと同じ方法で、CircleRectangle のインスタンス上でメソッドを呼び出すことができます。次のようになります。

let circle = Circle { radius: 5.0 };
let rectangle = Rectangle {
    width: 10.0,
    height: 20.0,
};

println!("Circle area: {}", circle.area());
println!("Rectangle area: {}", rectangle.area());

このコードは、こちらの Rust Playground のリンクで操作できます。