2个版本
0.1.1 | 2021年12月28日 |
---|---|
0.1.0 | 2021年12月25日 |
#2046 in 过程宏
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更难被误用。不需要程序员跟踪哪些数据已验证,哪些未验证,这项工作由编译器来完成。
这种模式应用广泛,例如
- 各种数据可能是
Validated
或Unvalidated
- 这可能在安全环境中特别有用,例如一个
SessionToken<Validated>
可能需要执行特定的数据库请求
- 这可能在安全环境中特别有用,例如一个
- GPIO引脚可以是
Enabled
或Disabled
,也可以是Input
或Output
(更多示例请参阅https://docs.rust-embedded.org/book/design-patterns/hal/gpio.html) - 在创建某些FFI代码的Rust安全接口时,您可能需要编码数据的状态(例如
Initialized/Uninitialized/Disposed
)
详细信息
由 union!
生成的每个特质都是 "封闭的",这意味着它不能在定义它们的模块外部实现。它们生成一个私有模块,其中包含一个超特质,该特质只针对宏中给定的类型实现。
每种类型都是一个空的枚举(即没有变体的枚举)因此不能被实例化。注意,这些类型几乎总是与 PhantomData
一起使用,因为它们只作为类型参数的具体值存在,但从不作为自身值(空枚举没有可能的值)。
依赖关系
~1.5MB
~36K SLoC