1 个不稳定版本
| 0.1.0 | 2022 年 8 月 2 日 |
|---|
#34 in #correct
27KB
248 行
由 Builder 衍生宏创建的构建器是编译时正确的。这意味着它只允许你构建给定结构体,只要你为它的所有必需字段提供了值。
如果字段没有被 Option 包装,则解释为必需字段。为了构建给定结构体,任何在 Option 内的字段都不被认为是必需的。例如在
pub struct MyStruct {
foo: String,
bar: Option<usize>,
}
foo 字段是必需的,而 bar 是可选的。**注意**:虽然 std::option::Option 也指向相同的类型,但目前这个宏不识别除 Option 之外的内容。使用 Builder 宏生成的构建器通过使用 const generics 来编码已初始化的集合来保证正确性。以下是一个例子。假设我们有一个具有两个必需字段和一个可选字段的 struct
pub struct MyStruct {
req1: String,
req2: String,
opt1: Option<String>
}
生成的构建器将是
pub struct MyStructBuilder<const P0: bool, const P1: bool> {
req1: Option<String>,
req2: Option<String>,
opt1: Option<String>,
}
P0 表示第一个必需参数是否已初始化。同样,P1 对第二个必需参数执行相同的操作。构建器的初始状态将是 MyStructBuilder<false, false>,并在首次初始化必需字段时,其相应的 const generic 参数将被设置为 true,这表示不同的状态。设置可选值不会改变状态,因此保持相同的 const generic 参数。当构建器达到 MyStructBuilder<true, true> 时,你才能在构建器上调用 build 函数。
因此,给定示例结构体的完整生成代码是
pub struct MyStruct {
req1: String,
req2: String,
opt1: Option<String>
}
pub struct MyStructBuilder<const P0: bool, const P1: bool> {
req1: Option<String>,
req2: Option<String>,
opt1: Option<String>,
}
impl MyStruct {
pub fn builder() -> MyStructBuilder<false, false> {
MyStructBuilder {
req1: None,
req2: None,
opt1: None,
}
}
}
impl<const P0: bool, const P1: bool> MyStructBuilder<P0, P1> {
pub fn req1(self, req1: String) -> MyStructBuilder<true, P1> {
MyStructBuilder {
req1: Some(req1),
req2: self.req2,
opt1: self.opt1,
}
}
pub fn req2(self, req2: String) -> MyStructBuilder<P0, true> {
MyStructBuilder {
req1: self.req1,
req2: Some(req2),
opt1: self.opt1,
}
}
pub fn opt1(self, opt1: String) -> MyStructBuilder<P0, P1> {
MyStructBuilder {
req1: self.req1,
req2: self.req2,
opt1: Some(opt1),
}
}
}
impl MyStructBuilder<true, true> {
pub fn build(self) -> MyStruct {
unsafe {
MyStruct {
req1: self.req1.unwrap_unchecked(),
req2: self.req2.unwrap_unchecked(),
opt1: self.opt1,
}
}
}
}
依赖关系
~1.5MB
~36K SLoC