ジェネリック境界

ジェネリックプログラミングをしていると、型パラメータが特定の機能を持っていることを規定するため、トレイトに境界(bound)を設ける必要があることがよくあります。例えば、以下の例では、引数のDisplayトレイトを用いてプリントを行うため、TDisplayを持っていることを規定しています。つまり、「TDisplayを実装 していなくてはならない 」という意味です。

// Define a function `printer` that takes a generic type `T` which
// must implement trait `Display`.
// `Display`トレイトを実装している`T`を引数として取る
// `printer`という関数を定義。
fn printer<T: Display>(t: T) {
    println!("{}", t);
}

境界は、ジェネリクスを全ての型ではなく、一定条件を満たす型に対してのみ適用するためにあります。つまり

訳注: <T: Display><T>の部分集合であることを意識すると、「境界」という言葉の意味がしっくり来ると思います。

struct S<T: Display>(T);

// Error! `Vec<T>` does not implement `Display`. This
// specialization will fail.
// エラー! `Vec<T>`は`Display`を実装していないため、この特殊化
// は失敗します。
let s = S(vec![1]);

境界のもう一つの効果は、ジェネリック型のインスタンスが、境界条件となるトレイトのメソッドにアクセスすることができるようになる点です。以下がその例です。

// A trait which implements the print marker: `{:?}`.
// プリント時のマーカー`{:?}`を実装するトレイト
use std::fmt::Debug;

trait HasArea {
    fn area(&self) -> f64;
}

impl HasArea for Rectangle {
    fn area(&self) -> f64 { self.length * self.height }
}

#[derive(Debug)]
struct Rectangle { length: f64, height: f64 }
#[allow(dead_code)]
struct Triangle  { length: f64, height: f64 }

// The generic `T` must implement `Debug`. Regardless
// of the type, this will work properly.
// ジェネリック型`T`は`Debug`トレイトを実装していなくてはならない。
// その限りにおいて、`T`がどのような具象型であろうとも次の関数は動作する。
fn print_debug<T: Debug>(t: &T) {
    println!("{:?}", t);
}

// `T` must implement `HasArea`. Any type which meets
// the bound can access `HasArea`'s function `area`.
// 「`T`は`HasArea`を実装していなくてはならない」という境界条件を
// 満たしていれば、`HasArea`の関数`area`にアクセスできる。
fn area<T: HasArea>(t: &T) -> f64 { t.area() }

fn main() {
    let rectangle = Rectangle { length: 3.0, height: 4.0 };
    let _triangle = Triangle  { length: 3.0, height: 4.0 };

    print_debug(&rectangle);
    println!("Area: {}", area(&rectangle));

    //print_debug(&_triangle);
    //println!("Area: {}", area(&_triangle));
    // ^ TODO: Try uncommenting these.
    // | Error: Does not implement either `Debug` or `HasArea`. 
    // ^ TODO: これらの行をアンコメントしてみましょう。
    // | Error: `Debug` も `HasArea`もどちらも実装されていません!
}

付け加えておくと、where句を用いて境界を適用することもできます。場合によってはこちらの記法を使用したほうが読みやすくなる場合もあります。

参照

std::fmt, 構造体(struct), トレイト

関連キーワード:  境界, 関数, Display, HasArea, 実装, エラー, Debug, Result, Rust, Example