#builder #builder-pattern #correct #compile-time #generator #options #generics

tidy-builder

tidy-builder 是一个编译时正确的构建器生成器

1 个不稳定版本

0.1.0 2022 年 8 月 2 日

#34 in #correct

MIT 许可证

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