Caution
お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。
トレイト境界 / where句
Rustのトレイト境界は、ジェネリクス型パラメータにトレイトの実装を要求する仕組みです。『T: Trait』の形式で指定し、複雑な場合は『where』句でまとめて書けます。
構文
use std::fmt::{Display, Debug};
// トレイト境界の基本構文です。
fn print_item<T: Display>(item: T) {
println!("{}", item);
}
// 複数のトレイト境界(+ で連結します)。
fn print_debug_and_display<T: Debug + Display>(item: T) {
println!("Display: {}", item);
println!("Debug: {:?}", item);
}
// where 句でトレイト境界を書く場合です(型パラメータが多い場合に見やすくなります)。
fn compare_and_print<T, U>(t: T, u: U) -> String
where
T: Display + PartialOrd,
U: Display + Debug,
{
format!("t={}, u={:?}", t, u)
}
// impl Trait 構文(簡略版)です。
fn print_it(item: &impl Display) {
println!("{}", item);
}
構文一覧
| 構文 | 概要 |
|---|---|
| fn foo<T: Trait>(x: T) | 型TにTraitの実装を要求します。 |
| fn foo<T: Trait1 + Trait2>(x: T) | Tに複数のトレイトを要求します。 |
| fn foo(x: &impl Trait) | impl Trait構文(関数の引数・戻り値に使えます)。 |
| fn foo() -> impl Trait | Traitを実装した何らかの型を返します(具体的な型は隠蔽)。 |
| where T: Trait1, U: Trait2 | where句で境界をまとめて書きます(可読性向上)。 |
| T: 'static | 型Tが'staticライフタイム(参照を含まない)を要求します。 |
| T: Clone + Send + Sync | スレッド間で安全に共有できる型に使うトレイト境界の例です。 |
サンプルコード
use std::fmt::Display;
// 最大値を返す関数(PartialOrd が必要です)。
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
// 複数のトレイト境界の例です。
fn describe<T: Display + std::fmt::Debug + Clone>(item: T) {
let cloned = item.clone();
println!("Display: {}", item);
println!("Debug: {:?}", cloned);
}
// where 句を使う例です(複数の型パラメータ)。
fn pair_display<T, U>(t: &T, u: &U)
where
T: Display,
U: Display,
{
println!("({}, {})", t, u);
}
// impl Trait で戻り値の型を隠蔽します(クロージャを返す典型例)。
fn make_adder(x: i32) -> impl Fn(i32) -> i32 {
move |n| n + x
}
// struct にトレイト境界を付ける例です。
struct Wrapper<T: Display> {
value: T,
}
impl<T: Display> Wrapper<T> {
fn new(value: T) -> Self {
Wrapper { value }
}
fn show(&self) {
println!("Wrapper({})", self.value);
}
}
fn main() {
// largest 関数を使います。
let numbers = vec![34, 50, 25, 100, 65];
println!("最大数: {}", largest(&numbers));
let chars = vec!['y', 'm', 'a', 'q'];
println!("最大文字: {}", largest(&chars));
// describe 関数を使います。
describe(42);
describe("hello");
// pair_display を使います。
pair_display(&3.14, &"world");
// make_adder でクロージャを返します。
let add5 = make_adder(5);
let add10 = make_adder(10);
println!("add5(3) = {}", add5(3)); // 8
println!("add10(3) = {}", add10(3)); // 13
// Wrapper を使います。
let w = Wrapper::new(100);
w.show();
let ws = Wrapper::new("hello");
ws.show();
}
概要
トレイト境界と『impl Trait』は異なる使い分けがあります。『impl Trait』は1つの型で十分な場合に簡潔に書けます。複数の引数が「同じ型」である必要がある場合はトレイト境界(『T: Trait』形式)を使います。
戻り値の『impl Trait』は関数ごとに異なる具体的な型を返せます。ただし分岐によって異なる型を返すことはできません(その場合は『Box<dyn Trait>』を使います)。
トレイトの定義と実装は『trait / impl Trait for 型』を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。