什么是Rust语言的类型状态Typestate模式?


Typestate模式是一种API设计模式,它将对象的运行时状态信息编码在其编译时的类型中。

当我们对一个对象的操作(如方法或函数),只有在该对象处于某些状态时才能使用:|

  • “只有检查过它是有效的 UTF-8 后,才能翻译缓冲区。”
  • “在文件句柄关闭后,您不得对文件句柄执行任何 I/O 操作。”
  • “这些消息只能在身份验证成功后发送给客户端,而不是在我们结束会话之后。”
  • “一旦你完成了动作 A,你必须先执行 B 或 C(但不能同时执行),然后才能执行 D。”

使用 typestate 模式,我们可以防止违反这些规则的代码编译,帮助程序员更早地发现错误并消除运行时检查的开销。
其他好处:
  • 它与IDE很好地互动,可以避免建议在某一状态下的非法操作。
  • 它可以消除运行时检查,使代码更快/更小。

这种模式在Rust中非常容易,几乎是显而易见的,以至于你可能已经写了使用它的代码,也许没有意识到这一点。有趣的是,它在大多数其他编程语言中很难实现--它们大多不能满足上述第2和/或第3项。

代码:

// 这里,我们还没有接触到一个文件。

// 这里 打开一个文件。
let file = std::fs::File::open(
"myfile.txt") ?

// 在这里,我们可以访问`file`,并且它已经打开。因为如果打开时
// 失败,我们会得到一个编译错误。


std::fs::File 标准库有两种状态:“打开open”和“关闭close”。如果您可以访问一个文件,它就是open:获得它的唯一方法是通过 open操作。

如果继续操作:

drop(file)。

// 从这里以后,试图在关闭后使用该文件将返回一个编译错误。
// file.read_to_string(&mut buffer); <-- 没有编译。

注意这里使用了drop方法:

pub fn drop<T>(value: T);

这个drop方法特殊:是通过值来获取参数,而不是通过引用(&T),也就是说,值参数会被移到drop函数中,而外部调用者则失去了对它的访问,因为传递的是值,不是引用或指针。

Rust 的“传递”值会导致先前的所有者无法访问该值,而在大多数其他语言中,情况并非如此,其他语言大多数是引用传递。