类型状态编程

类型状态的概念描述了将有关对象当前状态的信息编码到该对象的类型中。虽然这听起来有点深奥,但如果你在 Rust 中使用过构建器模式,那么你已经开始使用类型状态编程了!

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 对象。我们必须创建一个 FooBuilder,并在获得我们想要的 Foo 对象之前正确地初始化它。

这个最小示例编码了两种状态

  • FooBuilder,它表示“未配置”或“配置过程中的”状态
  • Foo,它表示“已配置”或“准备使用”状态。

强类型

由于 Rust 具有强类型系统,因此没有简单的方法可以神奇地创建 Foo 实例,或者在不调用 into_foo() 方法的情况下将 FooBuilder 转换为 Foo。此外,调用 into_foo() 方法会消耗原始的 FooBuilder 结构,这意味着它不能在没有创建新实例的情况下重复使用。

这使我们能够将系统状态表示为类型,并将状态转换所需的动作包含在将一种类型交换为另一种类型的函数中。通过创建 FooBuilder 并将其交换为 Foo 对象,我们已经完成了基本状态机的步骤。