デフォルト実装
CoddyのRustジャーニー「Object Oriented Programming」セクションの一部 — レッスン 31/61。
これまで、定義してきたすべてのトレイトメソッドは、シグネチャのみ、つまりボディ(本体)のない要件でした。しかし、トレイトを実装するほとんどの型が同じロジックを使用するとしたらどうでしょうか?Rustでは、トレイト定義の中で直接デフォルトの実装を提供することができます。
メソッドシグネチャをセミコロンで終わらせる代わりに、ボディを追加します:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
これで、Greetを実装するすべての型は、implブロックにコードを記述することなく、自動的にこのメソッドを使用できるようになります:
struct Guest;
impl Greet for Guest {}
let visitor = Guest;
println!("{}", visitor.greet()); // こんにちは!
空の impl Greet for Guest {} は、トレイトがすでに振る舞いを提供しているため有効です。構造体は単にトレイトに「オプトイン」し、デフォルトのメソッドをそのまま受け取ります。
このパターンは、ほとんどのケースで機能する共通の振る舞いを定義したい場合に便利です。異なる振る舞いが必要な型はデフォルトをオーバーライドでき(これについては次で説明します)、標準の振る舞いで問題ない型は同じコードを繰り返す必要がありません。
チャレンジ
簡単通知システムを構築することで、デフォルト実装の力を探求しましょう!標準的な動作をあらかじめ提供するトレイトを作成することで、型は反復的なコードを書くことなくその機能を利用できるようになります。
コードは3つのファイルに分けて構成します:
notifiable.rs: デフォルト実装を持つnotify(&self) -> Stringメソッドを含む、パブリックなNotifiableトレイトを定義します。デフォルトでは"You have a new notification!"を返すようにしてください。このトレイトを実装する型は、impl ブロック内に何も記述しなくても、自動的にこの動作を継承します。alerts.rs: トレイトを実装する2つのシンプルなパブリックユニット構造体を作成します:EmailAlert— 空の impl ブロックでNotifiableを実装します(デフォルトを使用)。SystemAlert— 同じく空の impl ブロックでNotifiableを実装します(デフォルトを使用)。
main.rs: モジュールをまとめ、両方の通知タイプが同じデフォルト動作を共有していることを示します。両方の構造体のインスタンスを作成し、それぞれのnotifyメソッドを呼び出してください。
メインファイルで、EmailAlert と SystemAlert を作成し、それぞれの通知を別々の行に出力してください。
出力は以下のようになるはずです:
You have a new notification!
You have a new notification!両方の型が同じ出力を生成することに注目してください。これがデフォルト実装の素晴らしさです!どちらの構造体も notify メソッドのためのコードを書く必要はありませんでしたが、両方とも完全に機能する通知動作を持っています。次のレッスンでは、型にカスタムの動作が必要な場合に、これらのデフォルトをオーバーライドする方法を学びます。
チートシート
トレイトのメソッドは、シグネチャだけでなくメソッド本体を提供することで、デフォルトの実装を持つことができます。
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
そのトレイトを実装する型は、コードを一切書くことなく、自動的にデフォルトの振る舞いを受け取ります。
struct Guest;
impl Greet for Guest {}
let visitor = Guest;
println!("{}", visitor.greet()); // Hello there!
トレイトがデフォルトの実装を提供している場合、空の impl ブロックは有効です。これにより、型は繰り返しを避けて共有された振る舞いを選択することができます。
自分で試してみよう
mod notifiable;
mod alerts;
use notifiable::Notifiable;
use alerts::{EmailAlert, SystemAlert};
fn main() {
// TODO: EmailAlert のインスタンスを作成する
// TODO: SystemAlert のインスタンスを作成する
// TODO: EmailAlert からの通知を表示する
// TODO: SystemAlert からの通知を表示する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。