类型状态编程(Typestate Programming)

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

1. pub mod foo_module {
2. #[derive(Debug)]
3. pub struct Foo {
4. inner: u32,
5. }

7. pub struct FooBuilder {
8. a: u32,
9. b: u32,
10. }

12. impl FooBuilder {
13. pub fn new(starter: u32) -> Self {
14. Self {
15. a: starter,
16. b: starter,
17. }
18. }

20. pub fn double_a(self) -> Self {
21. Self {
22. a: self.a * 2,
23. b: self.b,
24. }
25. }

27. pub fn into_foo(self) -> Foo {
28. Foo {
29. inner: self.a + self.b,
30. }
31. }
32. }
33. }

35. fn main() {
36. let x = foo_module::FooBuilder::new(10)
37. .double_a()
38. .into_foo();

40. println!("{:#?}", x);
41. }

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

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

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

强类型

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

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