Menu
Coddy logo textTech

トレイトを使用した型の返却

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 { ... } }
// }
challenge icon

チャレンジ

簡単

impl Trait 戻り値型を使って実装の詳細を隠す、グリーティングカード工場を作りましょう!具体的な型を呼び出し側に明かさずに、「something that can greet」を返す function を作成します。

コードを2つのファイルに分けて構成します。

  • greetings.rs&self を受け取り String を返す greet method を持つ、public な Greet trait を Define します。次に、message field(String)を持つ Card struct(public である必要はありません)を Create します。Card に対して Greet trait を Implement し、message を Return します。最後に、String parameter を受け取り impl Greet を返す、create_greeting という名前の public function を Create します。この function は、given message を持つ Card を Create して Return します。
  • main.rs:greetings module を取り込み、提供された input を使って create_greeting を Call します。ここで重要なのは、main file は Card type についてまったく know する必要がないことです。Greet を implements する何かを受け取ったことだけを know します。Return された value に対して greet method を 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 メソッドを呼び出して結果を出力する
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

Object Oriented Programmingのすべてのレッスン

自分で練習してみよう: Rustオンラインコンパイラ