验证就像一口漏水的大锅

让我们把注意力转移到 insert_subscriber 函数上。 假设它要求 form.name 非空,否则就会发生一些可怕的事情(例如,引发panic!)。

insert_subscriber 函数能安全地假设 form.name 非空吗?

仅从它的类型来看,显然不行:form.name 是一个字符串。它的内容没有任何保证。

如果你完整地看一下我们的程序,你可能会说:我们在请求处理程序的底层检查它是否非空,因此我们可以安全地假设每次调用 insert_subscriber 函数时,form.name 都会非空。

但为了做出这样的断言,我们必须从局部方法(让我们看看这个函数的参数)转变为全局方法(让我们扫描整个代码库)。

虽然对于像我们这样的小型项目来说,这或许可行,但检查函数 (insert_subscriber) 的所有调用点以确保事先执行了某个验证步骤,这在大型项目中很快就会变得不可行。

如果我们坚持使用 is_valid_name,唯一可行的方法就是在 insert_subscriber 函数中再次验证 form.name,以及所有其他要求名称非空的函数。

这才是我们真正确保不变量在需要的地方存在的唯一方法。

如果 insert_subscriber 变得太大,我们不得不将其拆分成多个子函数,会发生什么情况?如果它们需要不变量,那么每个子函数都必须执行验证以确保其成立。

正如您所见,这种方法无法扩展。

这里的问题是 is_valid_name 是一个验证函数:它告诉我们,在程序执行流程的某个时刻,一组条件得到了验证。 但是,关于输入数据中附加结构的信息并没有存储在任何地方。它会立即丢失。

程序的其他部分无法有效地重用它——它们被迫执行另一次时间点检查,导致代码库拥挤,每一步都充满噪音(且浪费)的输入检查。

我们需要的是一个解析函数——一个接受非结构化输入的例程,如果一组条件成立,则返回一个更结构化的输出,这个输出在结构上保证我们关心的不变量从那一刻起一直成立。

怎么做?

使用类型!