ジェネリック関数

Rust はジェネリクスをサポートしており、これにより、使用または格納される型に対してアルゴリズムやデータ構造 (ソートや二分木など)を抽象化できます。

// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

fn pick<T>(cond: bool, left: T, right: T) -> T {
    if cond { left } else { right }
}

fn main() {
    println!("picked a number: {:?}", pick(true, 222, 333));
    println!("picked a string: {:?}", pick(false, 'L', 'R'));
}
  • pick を単相化したバージョンを示すと役立つことがあります。ジェネリックな pick について説明する前に 示せば、ジェネリクスがどのようにコードの重複を減らせるかを示せますし、ジェネリクスについて説明した後に示せば、単相化がどのように機能するかを示せます。

    #![allow(unused)]
    fn main() {
    // Copyright 2023 Google LLC
    // SPDX-License-Identifier: Apache-2.0
    
    fn pick_i32(cond: bool, left: i32, right: i32) -> i32 {
        if cond { left } else { right }
    }
    
    fn pick_char(cond: bool, left: char, right: char) -> char {
        if cond { left } else { right }
    }
    }
  • Rust は、引数と戻り値の型に基づいて T の型を推論します。

  • この例では、T にはプリミティブ型の i32char だけを使っていますが、 ここではユーザー定義型を含む任意の型を使用できます。

    // Copyright 2023 Google LLC
    // SPDX-License-Identifier: Apache-2.0
    
    struct Foo {
        val: u8,
    }
    
    pick(false, Foo { val: 7 }, Foo { val: 99 });
  • これは C++ のテンプレートに似ていますが、Rust はジェネリック関数を即座に部分的にコンパイルするため、 その関数は制約に一致するすべての型に対して有効でなければなりません。たとえば、cond が false のときに pickleft + right を返すように変更してみてください。整数での pick のインスタンス化だけが使われる 場合でも、Rust はそれを無効とみなします。C++ ならこれを許します。

  • ジェネリックコードは、呼び出し箇所に基づいて非ジェネリックなコードへと変換されます。これはゼロコスト抽象化 です。抽象化なしでデータ構造を手で実装した場合と、まったく同じ結果が得られます。