Caution
お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。
trait / impl Trait for 型
Rustのトレイト(trait)は型が実装すべきメソッドを定義するインターフェースです。『trait』キーワードで定義し、『impl Trait for 型』で実装します。デフォルト実装も提供できます。
構文
// トレイトの定義です。
trait Greet {
// 必須メソッド: 実装側で定義します。
fn name(&self) -> &str;
// デフォルト実装: そのまま使えますが、オーバーライドも可能です。
fn greet(&self) {
println!("Hello, {}!", self.name());
}
}
struct English {
name: String,
}
// トレイトの実装です。
impl Greet for English {
fn name(&self) -> &str {
&self.name
}
// greet() はデフォルト実装を使います。
}
// 引数にトレイトを受け取ります(impl Trait 構文)。
fn introduce(item: &impl Greet) {
item.greet();
}
構文・概念一覧
| 構文・概念 | 概要 |
|---|---|
| trait Foo { fn method(&self); } | トレイトを定義します。 |
| fn method(&self) { ... } | トレイト内のデフォルト実装です。 |
| impl Foo for 型 { ... } | 型にトレイトを実装します。 |
| fn func(x: &impl Foo) | impl Trait構文: トレイトを実装した型を引数に取ります。 |
| fn func<T: Foo>(x: &T) | トレイト境界構文: 同上(ジェネリクス版)。 |
| fn func() -> impl Foo | トレイトを実装した型を戻り値にします。 |
| dyn Foo | 動的ディスパッチ: トレイトオブジェクトとして使います。 |
| Box<dyn Foo> | ヒープに置いたトレイトオブジェクトです(型消去)。 |
サンプルコード
trait Area {
fn area(&self) -> f64;
// デフォルト実装: 面積を整形して表示します。
fn describe(&self) {
println!("面積: {:.2}", self.area());
}
}
trait Perimeter {
fn perimeter(&self) -> f64;
}
struct Circle {
radius: f64,
}
struct Rectangle {
width: f64,
height: f64,
}
impl Area for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
}
impl Area for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
// describe() はデフォルト実装を使います。
}
impl Perimeter for Rectangle {
fn perimeter(&self) -> f64 {
2.0 * (self.width + self.height)
}
}
// impl Trait 構文: Areaトレイトを実装した型を引数に取ります。
fn print_area(shape: &impl Area) {
shape.describe();
}
// 動的ディスパッチ: 異なる型のトレイトオブジェクトをまとめて扱います。
fn largest_area(shapes: &[Box<dyn Area>]) -> f64 {
shapes.iter().map(|s| s.area()).fold(0.0_f64, f64::max)
}
fn main() {
let c = Circle { radius: 3.0 };
let r = Rectangle { width: 4.0, height: 6.0 };
// impl Trait で多態的に呼び出します。
print_area(&c);
print_area(&r);
// Box<dyn Trait> で異なる型をVecに混在させます。
let shapes: Vec<Box<dyn Area>> = vec![
Box::new(Circle { radius: 1.0 }),
Box::new(Rectangle { width: 3.0, height: 4.0 }),
Box::new(Circle { radius: 5.0 }),
];
for shape in &shapes {
shape.describe();
}
println!("最大面積: {:.2}", largest_area(&shapes));
}
概要
Rustのトレイトは、「孤児ルール(orphan rule)」により自分が定義した型か自分が定義したトレイトの少なくとも一方を持つ場合のみ実装できます。外部クレートの型に外部クレートのトレイトを実装することはできません。
『impl Trait』はコンパイル時に具体的な型が決まる静的ディスパッチ(モノモーフィズム)です。『dyn Trait』は実行時に型が決まる動的ディスパッチです。静的ディスパッチの方が高速ですが、動的ディスパッチは異なる型をコレクションにまとめられます。
DisplayトレイトとDebugトレイトは『Display トレイト / Debug トレイト』を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。