トレイトを使用した型の返却
CoddyのRustジャーニー「Object Oriented Programming」セクションの一部。レッスン 43/61。
トレイト境界を使って、function が受け入れられる型を制限しました。しかし、function が返すものについてはどうでしょうか? impl Trait 構文を使うと、concrete 型の名前を指定せずに、function が「このトレイトを実装する何らかの型」を返すことを指定できます。
構文を実際に見てみましょう:
trait Summary {
fn summarize(&self) -> String;
}
struct Article {
headline: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
self.headline.clone()
}
}
fn create_summary() -> impl Summary {
Article { headline: String::from("Breaking News!") }
}
戻り値の型 impl Summary は、呼び出し側に「Summary を実装する何かが得られる」と伝えます。呼び出し側はトレイトの任意の method を使用できますが、実際の型が Article であることを知る必要はありません。
これは、concrete 型が複雑な場合や、実装の詳細を隠したい場合に特に役立ちます。function は特定の型ではなく、機能(trait)を保証します。
ただし、重要な制限があります。function は、正確に1つの concrete な型を return しなければなりません。同じトレイトを implements する異なる型を条件に応じて返すことはできません。そのためにはトレイトオブジェクトが必要になります。これは後で学びます。
// これは動作します - 常に Article を返します
fn make_item() -> impl Summary {
Article { headline: String::from("Hello") }
}
// これはコンパイルできません - 2つの異なる型
// fn make_item(flag: bool) -> impl Summary {
// if flag { Article { ... } } else { Tweet { ... } }
// }
チャレンジ
簡単impl Trait 戻り値型を使って実装の詳細を隠す、グリーティングカード工場を作りましょう!具体的な型を呼び出し側に明かさずに、「something that can greet」を返す function を作成します。
コードを2つのファイルに分けて構成します。
greetings.rs:&selfを受け取りStringを返すgreetmethod を持つ、public なGreettrait を Define します。次に、messagefield(String)を持つCardstruct(public である必要はありません)を Create します。Cardに対してGreettrait を Implement し、message を Return します。最後に、Stringparameter を受け取りimpl Greetを返す、create_greetingという名前の public function を Create します。この function は、given message を持つCardを Create して Return します。main.rs:greetings module を取り込み、提供された input を使ってcreate_greetingを Call します。ここで重要なのは、main file はCardtype についてまったく know する必要がないことです。Greetを implements する何かを受け取ったことだけを know します。Return された value に対してgreetmethod を Call し、result を print します。
重要なポイントは、Card は module 内で private のままですが、callers は trait interface を通してそれを使用できるということです。function が promise するのは特定の type ではなく、capability(greeting)です。
出力には greeting が表示されます。
{message}たとえば、input が Happy Birthday! の場合:
Happy Birthday!また、input が Congratulations on your promotion! の場合:
Congratulations on your promotion!1つの input を受け取ります:greeting message。
自分で試してみよう
mod greetings;
use greetings::Greet;
fn main() {
// 入力から挨拶メッセージを読み取る
let mut input = String::new();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let message = input.trim().to_string();
// TODO: メッセージを使って create_greeting を呼び出す
// Note: 具体的な型はわかりません - Greet を実装していることだけがわかります!
// TODO: greet メソッドを呼び出して結果を出力する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
Object Oriented Programmingのすべてのレッスン
自分で練習してみよう: Rustオンラインコンパイラ