TL;DR

ジェネリクスのコードから、使われる具体的な型ごとにコードのコピーをコンパイル時に生成すること。Rust はジェネリックな型を実行時に解決せず、単相化する。

なぜ必要か

rustc 開発ガイドによると、単相化すると速いプログラムが得られる。Java のように型を実行時に解決する方式では、オブジェクトにアクセスするたびにポインタをたどる必要がある。

仕組み

rustc 開発ガイドによると、コンパイラは必要な具体的な型ごとに、ジェネリック関数のコードのコピーを作る。Effective Rust によると、トレイト境界の検査もこの単相化のときに行われる。Effective Rust 項目12 によると、C++ では同じことをテンプレートのインスタンス化と呼ぶ。

例

rustc 開発ガイドによると、コードで Vec<u64> と Vec<String> を使うと、生成されるバイナリには Vec のコードが 2 つ含まれる。

Go のジェネリクスは、純粋な単相化をしない。Go の interface と Generics の内部構造と進化によると、辞書と GC Shape Stenciling を使い、1 つの関数インスタンスを複数の型で動かしてコードサイズを節約している。

注意点

rustc 開発ガイドによると、コードのコピーを作るのでコンパイルに時間がかかり、バイナリも大きくなる。Effective Rust も、ジェネリクスは速いがバイナリサイズとコンパイル時間が増えるとして、トレイトオブジェクト(動的ディスパッチ)とのトレードオフを論じている。

関連

出典