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コードベースで推奨されるスタイルです。
チャレンジ
簡単where clauseを使って複雑なトレイト境界を読みやすく保つ、データ処理システムを構築しましょう!複数の型パラメーターを持つgeneric functionを作成し、それぞれに異なるトレイトを要求して、where構文を使って境界をきれいに整理します。
コードを2つのファイルに分けて構成します。
processor.rs:2つのpublicトレイトと、それら両方を使用するgeneric functionをDefineします。Summarize:&selfを受け取り、Stringを返すsummarymethodを持つトレイトValidate:&selfを受け取り、boolを返すvalidatemethodを持つトレイト
process_itemsというpublic generic functionをCreateします。whereclauseを使用して、first typeがCloneとSummarizeの両方をimplementする必要があり、second typeがValidateをimplementする必要があることを指定します。このfunctionでは、first itemのsummaryをPrintしてから、second itemが有効かどうかをPrintします。main.rs:必要なトレイトをimplementする2つのpublic structをCreateします。Article:publicなtitlefield(String)を持ちます。SummarizeをImplementしてArticle: {title}を返し、Cloneをderiveします。Form:publicなfilledfield(bool)を持ちます。ValidateをImplementしてfilledの値を返します。
process_itemsをCallします。
where clauseを使うと、すべての境界を山かっこ内に詰め込むよりも、function signatureがはるかに読みやすくなります。各型の要件がそれぞれ独自の行に表示されるため、各generic parameterに何が必要なのかを簡単に確認できます。
出力にはsummary followed by validation resultが表示されます。
Article: {title}
Valid: {true/false}たとえば、入力がRust Tipsとtrueの場合:
Article: Rust Tips
Valid: trueまた、入力がBreaking Newsとfalseの場合:
Article: Breaking News
Valid: false2つの入力、つまり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 を呼び出す
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rustオンラインコンパイラ