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