类型状态编程(Typestate Programming)

typestates的概念是指将有关对象当前状态的信息编码进该对象的类型中。虽然这听起来有点神秘,如果你在Rust中使用了建造者模式,你就已经开始使用类型状态编程了!

  1. pub mod foo_module {
  2. #[derive(Debug)]
  3. pub struct Foo {
  4. inner: u32,
  5. }
  6. pub struct FooBuilder {
  7. a: u32,
  8. b: u32,
  9. }
  10. impl FooBuilder {
  11. pub fn new(starter: u32) -> Self {
  12. Self {
  13. a: starter,
  14. b: starter,
  15. }
  16. }
  17. pub fn double_a(self) -> Self {
  18. Self {
  19. a: self.a * 2,
  20. b: self.b,
  21. }
  22. }
  23. pub fn into_foo(self) -> Foo {
  24. Foo {
  25. inner: self.a + self.b,
  26. }
  27. }
  28. }
  29. }
  30. fn main() {
  31. let x = foo_module::FooBuilder::new(10)
  32. .double_a()
  33. .into_foo();
  34. println!("{:#?}", x);
  35. }

在这个例子里,不能直接生成一个Foo对象。我们必须创造一个FooBuilder,且我们恰当地初始化FooBuilder后才能获取到我们需要的Foo对象。

这个最小的例子编码了两个状态:

  • FooBuilder,其表示了一个”没有被配置”,或者”正在配置”状态
  • Foo,其表示了一个”被配置”,或者”可以使用”状态。

强类型

因为Rust有一个强类型系统,没有简单的方法可以奇迹般地创造一个Foo实例或者不用调用into_foo()方法把一个FooBuilder变成一个Foo。另外,调用into_foo()方法消费了最初的FooBuilder结构体,意味着不创造一个新的实例它就不能被再次使用。

这允许我们去将我们系统的状态表示成类型,把状态转换必须的动作包括进交换两个类型的方法中。通过创造一个 FooBuilder,与一个 Foo 对象交换,我们已经使用了一个基本的状态机。