トレイトの実装
CoddyのRustジャーニー「Object Oriented Programming」セクションの一部 — レッスン 30/61。
トレイトの定義方法を学んだところで、構造体にそのコントラクト(契約)を満たさせる方法を見ていきましょう。トレイトを実装することで、抽象的な要求が具体的な振る舞いへと結びつきます。
構文には impl TraitName for StructName を使用します:
trait Speak {
fn speak(&self) -> String;
}
struct Dog {
name: String,
}
impl Speak for Dog {
fn speak(&self) -> String {
format!("{} says: Woof!", self.name)
}
}
impl Speak for Dogブロックは、DogがSpeakトレイトを実装していることをRustに伝えます。その内部で、実際のメソッド本体を記述します。ここで、犬がどのように鳴くかを定義します。
複数の型が同じトレイトを実装すると、トレイトの真価が発揮されます。
struct Cat {
name: String,
}
impl Speak for Cat {
fn speak(&self) -> String {
format!("{} says: Meow!", self.name)
}
}
Dog と Cat の両方が Speak 機能を共有するようになりましたが、それぞれが独自のメソッドを実装しています。どちらの型に対しても .speak() を呼び出すことができます:
let dog = Dog { name: String::from("Rex") };
let cat = Cat { name: String::from("Whiskers") };
println!("{}", dog.speak()); // Rex says: Woof!
println!("{}", cat.speak()); // Whiskers says: Meow!
これがRustにおけるポリモーフィズムの基礎です。異なる型、共通のインターフェース、そして独自の振る舞いです。
チャレンジ
簡単異なる型に対してトレイトを実装することで、トレイトを実際に動かしてみましょう!Describableトレイトを作成し、2つの異なる構造体に実装して、それぞれが独自の記述を提供できるようにします。
コードは以下の3つのファイルに整理します:
describable.rs:describe(&self) -> Stringメソッドを持つパブリックなDescribableトレイトを定義します。items.rs: トレイトを実装する2つのパブリックな構造体を作成します:titleフィールド(String)を持つパブリックなBooknameフィールド(String)を持つパブリックなMovie
Describableを実装する必要があります。main.rs: モジュールをまとめ、両方の構造体のインスタンスを作成し、それぞれのdescribeメソッドを呼び出して、同じトレイトが異なる型に対してどのように異なる出力を生成するかを示します。
Bookは自身をBook: {title}として記述し、Movieは自身をFilm: {name}として記述する必要があります。各型が同じコントラクトを満たしながら、独自の振る舞いを持っていることに注目してください!
メインファイルでは、提供された2つの入力を使用して本と映画を作成し、それぞれの記述を別々の行に出力してください。
出力は以下の形式に従う必要があります:
Book: {title}
Film: {name}例えば、入力が1984とInceptionの場合:
Book: 1984
Film: Inception入力として、本のタイトルと映画の名前の2つを受け取ります。
チートシート
構造体にトレイトを実装するには、impl TraitName for StructName構文を使用します。
trait Speak {
fn speak(&self) -> String;
}
struct Dog {
name: String,
}
impl Speak for Dog {
fn speak(&self) -> String {
format!("{} says: Woof!", self.name)
}
}
複数の型が、異なる振る舞いで同じトレイトを実装できます。
struct Cat {
name: String,
}
impl Speak for Cat {
fn speak(&self) -> String {
format!("{} says: Meow!", self.name)
}
}
let dog = Dog { name: String::from("Rex") };
let cat = Cat { name: String::from("Whiskers") };
println!("{}", dog.speak()); // Rex says: Woof!
println!("{}", cat.speak()); // Whiskers says: Meow!
これにより、Rustでのポリモーフィズムが可能になります。つまり、異なる型が独自の実装を持ちながら、同じインターフェースを共有できるようになります。
自分で試してみよう
mod describable;
mod items;
use describable::Describable;
use items::{Book, Movie};
fn main() {
// 入力を読み込む
let mut title = String::new();
std::io::stdin().read_line(&mut title).expect("Failed to read line");
let title = title.trim().to_string();
let mut name = String::new();
std::io::stdin().read_line(&mut name).expect("Failed to read line");
let name = name.trim().to_string();
// TODO: title を使用して Book インスタンスを作成する
// TODO: name を使用して Movie インスタンスを作成する
// TODO: book の説明を表示する
// TODO: movie の説明を表示する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。