複数の境界
CoddyのRustジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 41/61。
単一のトレイト境界では不十分な場合があります。both 印字でき、かつ要約を提供できるジェネリック型が必要になることもあります。Rust では、+ 構文を使って複数のトレイトを要求できます。
型が2つのトレイトを実装する必要があることを指定する方法は次のとおりです。
use std::fmt::Display;
trait Summary {
fn summarize(&self) -> String;
}
fn announce<T: Display + Summary>(item: T) {
println!("Breaking news: {}", item);
println!("Summary: {}", item.summarize());
}
境界 T: Display + Summary は、「T が Display と Summary のbothを実装しなければならない」という意味です。function 内では、両方のトレイトの機能を使用できます。たとえば、Display による {} を使った出力や、Summary の summarize() の呼び出しが可能です。
必要な数だけトレイトをチェーンできます。
fn process<T: Display + Summary + Clone>(item: T) {
// 印刷、要約、およびクローンが可能
}
このパターンは、function が複数の振る舞いに依存する場合に不可欠です。any 型を受け入れて動作することを期待するのではなく、必要な capabilities を正確に明示し、コンパイラがコンパイル時にそれを強制します。
チャレンジ
簡単複数の capabilities を持つことを必要とする product inspection system を構築しましょう!custom trait と standard trait の both を implements する型だけを受け入れる generic function を作成し、+ 構文によって複数の bounds を組み合わせる方法を示します。
コードを 2 つのファイルに分けて整理します。
product.rs:Inspectableという public trait を Define します。この trait には、&selfを受け取り、inspection details を含むStringを返すinspectという method があります。次に、public なGadgetstruct を作成し、public なname(String) とserial(u32) の fields を持たせます。Gadget はInspectable(Inspecting: {name}を返す)とstd::fmt::Display({name} (SN: {serial})として format する)の both を implements する必要があります。最後に、DisplayとInspectableの both を implements する any typeTを受け入れる、full_reportという public generic function を Create します。この function は 2 行を print します。first に{}formatter を使用して item を表示し、次にinspect()を calling した result を表示します。main.rs: product module を取り込み、提供された inputs を使用してGadgetinstance を Create します。gadget を使ってfull_reportを Call し、both の trait requirements を満たしていることを示します。
複数の bounds の power は、full_report function が both の traits の capabilities を使用できることです。つまり、item をきれいに表示し、inspection details を取得できます。しかも、すべてが compile time に保証されます。
output には display format と inspection result の both が表示されます。
{name} (SN: {serial})
Inspecting: {name}たとえば、inputs が Smartwatch と 98765 の場合:
Smartwatch (SN: 98765)
Inspecting: Smartwatch2 つの inputs を受け取ります。gadget name と serial number です(u32 として parse します)。
自分で試してみよう
mod product;
use product::{Gadget, full_report};
fn main() {
// 入力を読み取る
let mut name = String::new();
std::io::stdin().read_line(&mut name).expect("Failed to read line");
let name = name.trim().to_string();
let mut serial_input = String::new();
std::io::stdin().read_line(&mut serial_input).expect("Failed to read line");
let serial: u32 = serial_input.trim().parse().expect("Failed to parse serial");
// TODO: name と serial で Gadget のインスタンスを作成する
// TODO: あなたの gadget で full_report を呼び出す
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rustオンラインコンパイラ