Typestateプログラミング
typestates の概念では、オブジェクトの現在の状態に関する情報を、そのオブジェクトの型にエンコードします。これは少し難解に聞こえるかもしれませんが、Rust で Builder Pattern を使ったことがあるなら、すでに Typestateプログラミングを使い始めています!
pub mod foo_module {
#[derive(Debug)]
pub struct Foo {
inner: u32,
}
pub 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 = foo_module::FooBuilder::new(10)
.double_a()
.into_foo();
println!("{:#?}", x);
}
この例では、Foo オブジェクトを直接作成する方法はありません。必要な Foo オブジェクトを得るには、まず FooBuilder を作成し、それを適切に初期化する必要があります。
この最小限の例では、2 つの状態がエンコードされています。
FooBuilderは、「未設定」または「設定中」の状態を表しますFooは、「設定済み」または「使用可能」な状態を表します
強い型
Rust には Strong Type System があるため、Foo のインスタンスを魔法のように簡単に作成したり、into_foo() メソッドを呼ばずに FooBuilder を Foo に変えたりする簡単な方法はありません。さらに、into_foo() メソッドを呼び出すと元の FooBuilder 構造体は消費されるため、新しいインスタンスを作成しない限り再利用できません。
これにより、システムの状態を型として表現し、ある型を別の型に交換するメソッドに、状態遷移に必要な操作を組み込めます。FooBuilder を作成し、それを Foo オブジェクトに交換することで、基本的な状態機械のステップをたどったことになります。