型状態プログラミング
型状態の概念は、オブジェクトの現在の状態に関する情報を、そのオブジェクトの型にエンコードすることを説明しています。 これは、少し難解に思えますが、Rustのビルダーパターンを使ったことがあるならば、既に型状態プログラミングを使い始めています。
#[derive(Debug)] struct Foo { inner: u32, } struct FooBuilder { a: u32, b: u32, } impl FooBuilder { pub fn new(starter: u32) -> Self { Self { a: starter, b: starter, } } pub fn double_a(self) -> Self { Self { a: self.a * 2, b: self.b, } } pub fn into_foo(self) -> Foo { Foo { inner: self.a + self.b, } } } fn main() { let x = FooBuilder::new(10) .double_a() .into_foo(); println!("{:#?}", x); }
この例では、Foo
オブジェクトを直接作る方法はありません。必要なFoo
オブジェクトを取得する前に、FooBuilder
を作り、正しく初期化しなければなりません。
この最小限の例は、2つの状態をエンコードしています。
FooBuilder
は、「未設定」もしくは「設定中」の状態を表現します。Foo
は、「設定済み」もしくは「使用準備完了」の状態を意味します。
強い型
Rustは強い型付けシステムを持っています。Foo
のインスタンスを魔法のように作成したり、
into_foo()
メソッドを呼び出すことなしにFooBuilder
からFoo
に変換したりする、簡単な方法はありません。
さらに、into_foo()
メソッドは、オリジナルのFooBuilder
構造体を消費します。
これは、新しいインスタンスを作成しないと、再利用ができないことを意味します。
このことにより、システムの状態を型として表現することが可能になります。
そして、状態遷移に必要なアクションを、ある型と別の型とを交換するメソッドに取り入れることができます。
FooBuilder
を作成し、Foo
オブジェクトに交換することで、基本的なステートマシンのステップを見てきました。