验证就像一口漏水的大锅
让我们把注意力转移到 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
是一个验证函数:它告诉我们,在程序执行流程的某个时刻,一组条件得到了验证。
但是,关于输入数据中附加结构的信息并没有存储在任何地方。它会立即丢失。
程序的其他部分无法有效地重用它——它们被迫执行另一次时间点检查,导致代码库拥挤,每一步都充满噪音(且浪费)的输入检查。
我们需要的是一个解析函数——一个接受非结构化输入的例程,如果一组条件成立,则返回一个更结构化的输出,这个输出在结构上保证我们关心的不变量从那一刻起一直成立。
怎么做?
使用类型!