Menu
Coddy logo textTech

where 句

CoddyのRustジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 42/61。

トレイト境界を追加していくと、関数シグネチャが読みにくくなることがあります。複数のジェネリックパラメータを持ち、それぞれが複数のトレイトを必要とする関数を考えてみましょう。

fn complex_operation<T: Display + Clone, U: Debug + Summary>(first: T, second: U) {
    // ...
}

これは動作しますが、シグネチャが混み合ってきています。where句を使うと、パラメータリストの後にトレイト境界を移動できるため、よりすっきりした書き方になります。

fn complex_operation<T, U>(first: T, second: U)
where
    T: Display + Clone,
    U: Debug + Summary,
{
    // ...
}

どちらのバージョンも機能的には同一です。where句は読みやすさのためだけのものです。各型の要件がそれぞれ独自の行に記述されるため、各ジェネリックパラメーターに必要なものを簡単に確認できます。

where句は、境界が複雑な場合やジェネリックパラメータが多数ある場合に、特に役立ちます。function名とパラメータをひと目で確認でき、制約は下に別途一覧表示されます。これは、境界が単純な T: Trait を超える場合に、ほとんどのRustコードベースで推奨されるスタイルです。

challenge icon

チャレンジ

簡単

where clauseを使って複雑なトレイト境界を読みやすく保つ、データ処理システムを構築しましょう!複数の型パラメーターを持つgeneric functionを作成し、それぞれに異なるトレイトを要求して、where構文を使って境界をきれいに整理します。

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

  • processor.rs:2つのpublicトレイトと、それら両方を使用するgeneric functionをDefineします。
    • Summarize&selfを受け取り、Stringを返すsummary methodを持つトレイト
    • Validate&selfを受け取り、boolを返すvalidate methodを持つトレイト
    その後、異なるgeneric typeの2つのパラメーターを受け取る、process_itemsというpublic generic functionをCreateします。where clauseを使用して、first typeがCloneSummarizeの両方をimplementする必要があり、second typeがValidateをimplementする必要があることを指定します。このfunctionでは、first itemのsummaryをPrintしてから、second itemが有効かどうかをPrintします。
  • main.rs:必要なトレイトをimplementする2つのpublic structをCreateします。
    • Article:publicなtitle field(String)を持ちます。SummarizeをImplementしてArticle: {title}を返し、Cloneをderiveします。
    • Form:publicなfilled field(bool)を持ちます。ValidateをImplementしてfilledの値を返します。
    入力された値を使ってArticleとFormをCreateし、その後、両方を使ってprocess_itemsをCallします。

where clauseを使うと、すべての境界を山かっこ内に詰め込むよりも、function signatureがはるかに読みやすくなります。各型の要件がそれぞれ独自の行に表示されるため、各generic parameterに何が必要なのかを簡単に確認できます。

出力にはsummary followed by validation resultが表示されます。

Article: {title}
Valid: {true/false}

たとえば、入力がRust Tipstrueの場合:

Article: Rust Tips
Valid: true

また、入力がBreaking Newsfalseの場合:

Article: Breaking News
Valid: false

2つの入力、つまりarticle titleとformがfilledかどうかを受け取ります(boolとしてparseします)。

自分で試してみよう

mod processor;

use processor::{Summarize, Validate, process_items};
use std::io;

// TODO: 公開の構造体 Article を定義し、公開の title フィールド (String) を持たせる
// Article に Clone を導出する

// TODO: Article に Summarize を実装する
// summary メソッドは "Article: {title}" を返す必要がある

// TODO: 公開の構造体 Form を定義し、公開の filled フィールド (bool) を持たせる

// TODO: Form に Validate を実装する
// validate メソッドは filled の値を返す必要がある

fn main() {
    let mut title = String::new();
    io::stdin().read_line(&mut title).expect("Failed to read line");
    let title = title.trim().to_string();

    let mut filled_input = String::new();
    io::stdin().read_line(&mut filled_input).expect("Failed to read line");
    let filled: bool = filled_input.trim().parse().expect("Failed to parse bool");

    // TODO: 与えられた title で Article を作成する
    
    // TODO: 与えられた filled の値で Form を作成する
    
    // TODO: article と form を渡して process_items を呼び出す
}
quiz icon腕試し

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

オブジェクト指向プログラミングのすべてのレッスン

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