#generate #helper #macro #user-input #type #union #validate

typetrait

生成类型状态编程的类型辅助宏

2个版本

0.1.1 2021年12月28日
0.1.0 2021年12月25日

#2046 in 过程宏

MIT 协议

12KB
185

typetrait

生成更多类型安全API的样板代码的辅助宏。

例如,考虑一个接收用户输入的API

struct Data {
  email: String,
  age: u8,
}

fn get_data() -> Data {
  todo!()
}

我们可能想要确保在开始使用之前,data.email 包含一个有效的电子邮件地址。通过使 Data 泛型于 "是否已验证" 可以实现这一点。

// generates a trait called Status, and types Validated and Unvalidated that implement Status
union! {
  pub Status = Validated | Unvalidated
}

struct Data<T: Status> {
  _marker: std::marker::PhantomData<T>,
  email: String,
  age: u8,
}

有了这种设置,我们现在可以在编译时防止未验证的数据被使用

// data received from user input is considered unvalidated
fn get_data() -> Data<Unvalidated> {
  todo!()
}

// only validated data should be used by the rest of the application
fn handle_data(data: Data<Validated>) {
  todo!()
}

// convert unvalidated data to validated data (by validating it!)
fn validate(Data { email, age, .. }: Data<Unvalidated>) -> Result<Data<Validated>, &'static str> {
  if !is_valid_email(&email) {
    return Err("invalid email");
  }
  
  Ok(Data {
    email,
    age,
    _marker: std::marker::PhantomData,
  })
}

现在这个API更难被误用。不需要程序员跟踪哪些数据已验证,哪些未验证,这项工作由编译器来完成。

这种模式应用广泛,例如

  • 各种数据可能是 ValidatedUnvalidated
    • 这可能在安全环境中特别有用,例如一个 SessionToken<Validated> 可能需要执行特定的数据库请求
  • GPIO引脚可以是 EnabledDisabled,也可以是 InputOutput(更多示例请参阅https://docs.rust-embedded.org/book/design-patterns/hal/gpio.html
  • 在创建某些FFI代码的Rust安全接口时,您可能需要编码数据的状态(例如 Initialized/Uninitialized/Disposed

详细信息

union! 生成的每个特质都是 "封闭的",这意味着它不能在定义它们的模块外部实现。它们生成一个私有模块,其中包含一个超特质,该特质只针对宏中给定的类型实现。

每种类型都是一个空的枚举(即没有变体的枚举)因此不能被实例化。注意,这些类型几乎总是与 PhantomData 一起使用,因为它们只作为类型参数的具体值存在,但从不作为自身值(空枚举没有可能的值)。

依赖关系

~1.5MB
~36K SLoC