类型状态编程

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

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 对象,我们已经完成了基本状态机的步骤。