ジェネリクス

ジェネリクスとは、型と関数の機能をより汎用的に使えるようにするための機能です。これはあらゆる局面でコードの重複を避けるために非常に役立ちますが、多少構文が複雑になります。すなわち、ジェネリック型を使いこなすには、どのようなジェネリック型がきちんと機能するかに細心の注意を払う必要があります。最もシンプルで一般的なジェネリクスの利用法は型パラメータです。

ジェネリック型の型パラメータにはかぎ括弧とアッパーキャメルケース<Aaa, Bbb, ...>)が使われます。ジェネリックな型パラメータはたいていの場合<T>で示されます。Rustの場合、「ジェネリクス」には「1つ以上のジェネリックな型パラメータ<T>を受け付けるもの」という意味もあります。ジェネリックな型パラメータを指定された場合、それは必ずジェネリック型になり、そうでなければ必ず非ジェネリック型、すなわち具象型になります。

例として、あらゆる型の引数Tをとる ジェネリック関数 fooを定義すると

fn foo<T>(arg: T) { ... }

となります。Tはジェネリックな型パラメータに指定されているので、この場所で(arg: T)のように使用するとジェネリック型として扱われます。これはTという構造体がそれ以前に定義されていても同様です。

では、手を動かしながらジェネリック型の構文を体験していきましょう。

// `A`という具象型
struct A;

// `Single`という型を定義する際に`A`を使用していますが、その最初の使用よりも先に
// `<A>`がないため、また、`A`自身も具象型であるため、`Single`は具象型となります。
struct Single(A);
//            ^ `Single`による`A`の一番最初の使用はここ。

// ここでは`<T>`が一番初めの`T`の使用よりも先に来ています。
// よって`SingleGen`はジェネリック型となります。
// なぜならば型パラメータ`T`がジェネリックだからです。
// `T`はどんな型にもなりえるため、上で定義した`A`を受け取ることもできます。
struct SingleGen<T>(T);

fn main() {
    // `Single`は具象型で、`A`のみを受け取ります。
    let _s = Single(A);
    
    // `_char`という名の変数を生成します。これは`SingleGen<char>`
    // という型で、値は`SingleGen('a')`となります。
    // ここでは、`SingleGen`には明示的な型パラメータが与えられています。
    let _char: SingleGen<char> = SingleGen('a');

    // `SingleGen`型の変数には明示的に型パラメータを与えなくても構いません。
    let _t    = SingleGen(A); // 上で定義した`A`を使用。
    let _i32  = SingleGen(6); // `i32`を使用。
    let _char = SingleGen('a'); // `char`を使用。
}

参照

構造体