サンプルコードで身につける: 静的ディスパッチと動的ディスパッチ — ジェネリクスとの使い分け
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1同じトレイトを2通りの方法で呼ぶ
同じ Bell の play() を、ジェネリクス(静的)と &dyn(動的)の両方で呼び比べる最小例です。実行結果は同じでも、呼び先の関数が決まるのがコンパイル時か実行時かという内部の仕組みが違います。まず2つの書き方を並べて、シグネチャの形の違いを見比べてください。
2静的ディスパッチ — 型ごとに専用関数が作られる
report<T: Area> は、Circle で呼べば Circle 専用、Square で呼べば Square 専用の関数としてコンパイル時に展開されます(単相化)。呼び出しは直接呼び出しになるため実行時コストはゼロです。その代わり、使った型の数だけコードが複製されるという交換条件があります。
3混在リストは動的ディスパッチの出番
Vec<T> の T は1つの型に固定されるため、ジェネリクスでは Jp と En を同じ Vec に入れられません。Box<dyn Greet> なら型を消して混在でき、ループの中で要素ごとに実装が実行時に表引きされます。「異なる型を同じ入れ物に入れたい」が dyn を選ぶ最大の理由です。
4実行時の条件で実装を選ぶ
予算という実行時の値によって、同じ変数 route に異なる型への参照を入れる例です。ジェネリクスの T はコンパイル時に1つへ決まるので、この「if でどちらかを選ぶ」は &dyn でしか書けません。条件によるアルゴリズムの切り替えは動的ディスパッチの典型的な使いどころです。
5通知チャネルの差し替え
通知先(メール / Slack)を設定文字列で切り替える、実務でよくある構成です。設定は実行時にしか分からないため、ファクトリ関数は Box<dyn Notifier> を返す動的ディスパッチを選びます。呼び出し側は send() を呼ぶだけで、通知先の種類が増えても変更不要です。