#编译时 #不变性 #属性 #验证 #有效 #强制执行 #无效

有效性

编译时强制执行任意属性的类型安全包装器

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 次下载

MIT 许可证

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);
}

这可以工作,但我们“隐藏”了一个特定的不变性,从编译器那里:不能使用可能返回 falseEmail 调用 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(()),
    }
  }
}

无运行时依赖

功能