タイプステートパターン

#[entry]
fn main() -> ! {
    let p = Peripherals::take().unwrap();
    let gpio0 = p0::Parts::new(p.P0);

    let pin: P0_01<Disconnected> = gpio0.p0_01;

    // let gpio0_01_again = gpio0.p0_01; // エラー、移動済み。
    let mut pin_input: P0_01<Input<Floating>> = pin.into_floating_input();
    if pin_input.is_high().unwrap() {
        // ...
    }
    let mut pin_output: P0_01<Output<OpenDrain>> = pin_input
        .into_open_drain_output(OpenDrainConfig::Disconnect0Standard1, Level::Low);
    pin_output.set_high().unwrap();
    // pin_input.is_high(); // エラー、移動済み。

    let _pin2: P0_02<Output<OpenDrain>> = gpio0
        .p0_02
        .into_open_drain_output(OpenDrainConfig::Disconnect0Standard1, Level::Low);
    let _pin3: P0_03<Output<PushPull>> =
        gpio0.p0_03.into_push_pull_output(Level::Low);

    loop {}
}
  • この例では、ピンを表すタイプはCopyCloneも実装していません。そのため、ただ一つのインスタンスだけが存在可能です。ピンがポート構造体からムーブされると、他の誰もそのピンにアクセスすることはできなくなります。
  • ピンの設定を変更することは古いピンのインスタンスを消費することになります。そのため、それ以降は古いインスタンスを使い続けることはできなくなります。
  • 変数の型はその状態を表すようになっています。例えば、この例では型がGPIOピンの状態を表しています。このようにステートマシンをタイプシステムに織り込むことで、正しい設定をせずにピンを使ってしまうことがなくなります。不正な状態遷移に関してはコンパイル時に発見されるようになります。
  • インプットピンに対してis_highを呼び出すことは可能で、アウトプットピンに対してset_highを呼び出すことも可能です。しかし、その逆の組み合わせは不可能です。
  • 多くのHALクレートがこのパターンを用いています。