ゼロコスト抽象化
型状態もまた、ゼロコスト抽象化の優れた例です。これは、特定の振る舞いをコンパイル時の実行や解析へ移せる能力を指します。これらの型状態は実際のデータを一切持たず、代わりにマーカーとして使用されます。データを持たないため、実行時のメモリ上には実際の表現を持ちません。
use core::mem::size_of;
let _ = size_of::<Enabled>(); // == 0
let _ = size_of::<Input>(); // == 0
let _ = size_of::<PulledHigh>(); // == 0
let _ = size_of::<GpioConfig<Enabled, Input, PulledHigh>>(); // == 0
ゼロサイズ型
struct Enabled;
このように定義された構造体は、実際のデータを含まないため、ゼロサイズ型と呼ばれます。これらの型はコンパイル時には「実在する」かのように振る舞い、コピーしたり、ムーブしたり、参照を取ったりできますが、オプティマイザによって完全に取り除かれます。
このコードスニペットでは:
pub fn into_input_high_z(self) -> GpioConfig<Enabled, Input, HighZ> {
self.periph.modify(|_r, w| w.input_mode().high_z());
GpioConfig {
periph: self.periph,
enabled: Enabled,
direction: Input,
mode: HighZ,
}
}
返される GpioConfig は実行時には決して存在しません。この関数の呼び出しは、一般に単一のアセンブリ命令、つまり定数のレジスタ値をレジスタ位置に格納する処理にまで落とし込まれます。これは、私たちが開発した型状態インターフェースがゼロコスト抽象化であることを意味します。GpioConfig の状態を追跡するために追加の CPU、RAM、コード領域を一切消費せず、直接的なレジスタアクセスと同じマシンコードに変換されます。
ネスト
一般に、これらの抽象化は好きなだけ深くネストできます。使用されるすべての構成要素がゼロサイズ型である限り、構造全体は実行時には存在しません。
複雑な構造や深くネストした構造では、可能な状態の組み合わせをすべて定義するのは面倒になることがあります。そのような場合には、すべての実装を生成するためにマクロを使用できます。