6 个版本
0.3.1 | 2023 年 1 月 19 日 |
---|---|
0.3.0 | 2023 年 1 月 18 日 |
0.2.0 | 2023 年 1 月 18 日 |
0.1.2 | 2022 年 5 月 6 日 |
0.1.1 | 2022 年 2 月 3 日 |
#134 在 电子邮件 类别中
每月 23 次下载
12KB
59 代码行
有效性
编译时强制执行任意不变性
通常,使无效状态不可表示是一个好主意。这通常可以通过类型系统实现,但类型系统的能力有限。
例如,想象你正在尝试表示一个用户的电子邮件地址。你可能写一个这样的结构体
struct Email(pub String);
但是你不想允许任何旧字符串,你可能想验证它实际上是一个有效的电子邮件地址。所以你的代码可能看起来像这样
fn validate_email(email: &Email) -> bool {
// ...
}
fn handle_email(email: Email) {
if !validate_email(&email) {
panic!("invalid email"); // or return Err(...)
}
do_stuff_with_valid_email(email: Email);
}
fn do_stuff_with_valid_email(email: Email) {
println!("definitely a valid email: {}", email.0);
}
这可以工作,但我们“隐藏”了一个特定的不变性,从编译器那里:不能使用可能返回 false
的 Email
调用 do_stuff_with_email
。
但是,使用 validity
,我们可以对代码进行一些小的修改,让编译器了解这个不变性
- 首先,定义电子邮件“有效”的含义
impl Validate for Email {
type Error = &'static str; // preferably a more meaningful error type
fn is_valid(&self) -> Result<(), Self::Error> {
match validate_email(self) {
true => Ok(()),
false => Err("invalid email"),
}
}
}
- 然后,我们将
do_stuff_with_valid_email
标记为需要一个有效的电子邮件地址
fn do_stuff_with_valid_email(email: Valid<Email>) {
// ...
}
获取 Valid<Email>
的唯一方法是通过 validate
函数并处理任何潜在的错误
fn handle_email(email: Email) {
match email.validate() {
Ok(valid_email) => do_stuff_with_valid_email(valid_email),
Err(_) => println!("uh oh!"),
}
}
太好了!我们现在 不能忘记验证 电子邮件,因为没有其他方法可以创建 Valid<T>
。
警告 - 确定性
注意,此实现假设 is_valid
是确定性的(即每次调用都给出相同的结果)。例如,以下实现可能允许在 Valid<T>
中存在无效值
impl Valid for MyType {
type Error = ();
fn is_valid(&self) -> Result<(), ()> {
match rand::random() {
true => Ok(()),
false => Err(()),
}
}
}