本文へスキップ
BecomeCoder

Rustコース · 第9章 ジェネリクスとトレイト · レッスン38

トレイト境界 ― ジェネリクスに条件を付ける

ブラウザで完結

導入

レッスン36で「T の値を比較したいけど、どんな型でも比較できるとは限らない」という問題が残りました。トレイトを使えば「T は○○できる型に限る」と条件を付けられます。これが トレイト境界(trait bound) で、ジェネリクスとトレイトが合流する要の部分です。

説明

「要約できる型なら何でも受け取る」関数を書きます。

trait Summary {
    fn summarize(&self) -> String;
}

// T は「Summary を実装している型」に限る、という境界を付ける
fn notify<T: Summary>(item: &T) {
    println!("速報! {}", item.summarize());
}

// impl Trait 構文(同じ意味の短い書き方)
fn notify2(item: &impl Summary) {
    println!("速報! {}", item.summarize());
}
  • <T: Summary> … 「型 TSummary トレイトを実装していること」という条件(境界)。この関数には、summarize() できる型しか渡せません。
  • 逆に言うと、itemSummary を実装していることが保証されるので、関数の中で安心して item.summarize() を呼べます。
  • &impl Summary … 同じ意味の短縮記法。

複数の条件は + で足せます。標準的な例を見ましょう。

use std::fmt::Display;

// T は「比較できて(PartialOrd)、表示できる(Display)」型に限る
fn largest<T: PartialOrd + Display>(list: &[T]) -> &T {
    let mut biggest = &list[0];
    for item in list {
        if item > biggest {      // PartialOrd があるので > で比較できる
            biggest = item;
        }
    }
    biggest
}

fn main() {
    let numbers = vec![34, 50, 25, 100, 65];
    println!("最大は {}", largest(&numbers));   // 100

    let chars = vec!['y', 'm', 'a', 'q'];
    println!("最大は {}", largest(&chars));      // y
}
  • T: PartialOrd + Display … 「比較でき、かつ表示できる型」。この境界があるおかげで、関数内で >(比較)と {}(表示)が使えます。
  • largesti32 のベクタにも char のベクタにも使えます。1つのコードで、条件を満たすあらゆる型に対応。

トレイト境界は「ジェネリクスの自由さ」と「型の安全性」を橋渡しします。「どんな型でもいい、ただし○○できるなら」——この絞り込みで、汎用的なのに安全なコードが書けるのです。これが Rust の抽象化の到達点であり、標準ライブラリの IteratorFrom/Into など、あらゆる場所でこの仕組みが使われています。

試すには

largest からトレイト境界 PartialOrd を外すと、item > biggest の比較が「その型が比較できるか分からない」とエラーになります。境界が「関数内で使える操作」を保証していることが分かります。「汎用にしたいが、この能力は必要」という設計を、境界で表現してみましょう。

実際に動かしてみよう

下のエディタにRustを書いて「実行」を押すと、学習用シミュレータが println! の出力を表示します(本物のrustcではなく、教材の範囲を再現した軽量エンジンです)。struct・enum・match・トレイト・Vec/String/HashMap・Option/Result なども動きます。本文の例を書き換えて動かしてみましょう(所有権・借用チェッカーのコンパイルエラーは再現しないため、動きの確認用として使ってください)。

Rust — ブラウザ内で実行(学習用シミュレータ)

Rustの教材サブセットを動かす学習用シミュレータを読み込みます(本物のrustcではなく、動きを再現した軽量な自作エンジンです)。
スクロールして表示された時点でも自動で読み込まれます。