#derive-builder #builder-pattern #macro #type-safe #compile-time #infallible #struct

typesafe-builders

不可故障的编译时检查构建器,适用于您的结构体

8 个版本 (4 个破坏性更改)

0.5.0 2023 年 5 月 8 日
0.4.1 2023 年 5 月 6 日
0.3.0 2023 年 4 月 8 日
0.2.1 2023 年 4 月 4 日
0.1.3 2023 年 4 月 2 日

#1811 in Rust 模式


framy 中使用

GPL-3.0-only

21KB
321


类型安全构建器模式

不可故障的编译时检查构建器,适用于您的结构体。

License: GPL v3

不再需要担心您的构建器上的 build 调用是否会返回 OkErr。也许你忘记设置一个字段? typesafe-builders 通过使用 Rust 类型系统来确保正确的使用。

示例

use typesafe_builders::prelude::*;

fn main() {
	#[derive(Builder)]
	struct Point {
		#[builder(constructor)]
		x: u8,
		y: u8,
		#[builder(optional)]
		z: Option<u8>,
	}

	// `builder` requires `x` since it is marked as `constructor`.
	let builder = Point::builder(1);
	// These do not compile:
	// partial.x(6); 		// `x` is already set
	// partial.build();		// `y` is not set

	// `build` is only available once all required fields are set:
	let result = builder.y(2).build();

	assert_eq!(result.x, 1);
	assert_eq!(result.y, 2);
	assert_eq!(result.z, None);
}

已知缺点

我只推荐在 内部使用。最好不要将这些构建器类型作为你的 crate 的 API,因为它们看起来非常丑陋且冗长。例如

use typesafe_builders::prelude::*;

#[derive(Builder)]
struct Point {
	x: u8,
	y: u8,
	z: u8,
}

// Ugly type name here... and it only gets worse for const-generics etc.
fn preset() -> GenericPointBuilder<false, false, true> {
	Point::builder().z(0)
}

fn main() {
	let partial = preset();
	let point = partial.x(1).y(2).build();
}

如果你知道如何改进这个,请打开一个 MR/Issue。

字段属性

属性可以组合。那些不能一起工作的将在编译时抛出显式错误。重复总是错误。

可选

字段可以设置,但不必设置。需要字段类型为 Default

use typesafe_builders::prelude::*;

#[derive(Builder)]
pub struct Struct {
	#[builder(optional)]
	x: u8,
}

fn main() {
	// without x
	Struct::builder().build();
	 // with x
	Struct::builder().x(4).build();
}

构造函数

在构建器构造时要求设置字段。

use typesafe_builders::prelude::*;

#[derive(Builder)]
pub struct Struct {
	#[builder(constructor)]
	x: u8,
}

fn main() {
	Struct::builder(4).build();
	// does not work:
	// Struct::builder(4).x(5).build();
}

降级

将类型降级为其第一个泛型。简化了对 OptionBox 等的使用。要求降级后的类型可以被 into 成原始类型。对所有只有一个泛型参数的类型都有效。

use typesafe_builders::prelude::*;

#[derive(Builder)]
pub struct Struct {
	#[builder(decay)]
	x: Option<u8>,
}

fn main() {
	// Use `4` of `Some(4)`
	Struct::builder().x(4).build();
}

它是如何工作的?

常量泛型单热位字段。你得到的是类似这样的东西

pub struct Builder<const x_set: bool, const y_set: bool> {
	x: Option<u8>,
	y: Option<u8>,
}

impl<const y_set: bool> Builder<false, y_set> {
    fn set_x(self, x: u8) -> Builder<true, y_set,> {
        unimplemented!()
    }
}

impl<const x_set: bool> Builder<x_set, false> {
    fn set_y(self, y: u8) -> Builder<x_set, true> {
        unimplemented!()
    }
}

// The build function is only available once all fields are set:
impl Builder<true, true> {
    fn build() {

    }
}

更多示例

生命周期

它们按预期工作

use typesafe_builders::prelude::*;

#[derive(Builder)]
pub struct Struct<'a, 'b, 'c> {
	x: &'a Box<&'b Option<&'c str>>, // yikes
}

fn main() {
	Struct::builder().x(&Box::new(&Some("hi"))).build();
}

泛型

按预期工作,但尚未支持默认值。

mod other {
	use typesafe_builders::prelude::*;

	#[derive(Builder)]
	pub struct Struct<T: Clone> {
		y: Option<T>,
	}
}

fn main() {
	other::Struct::<u8>::builder().y(Some(4)).build();
}

常量泛型

按预期工作,但尚未支持默认值。

mod other {
	use typesafe_builders::prelude::*;

	#[derive(Builder)]
	pub struct Struct<const LEN: usize> {
		x: [u8; LEN],
	}
}

fn main() {
	other::Struct::<1>::builder().x([1]).build();
}

待办事项

  • 生命周期
  • 泛型
    • 界限
    • 带有默认值
  • 常量泛型
    • 带有默认值
  • 添加 optional 字段。
  • 添加 rename 字段属性。
  • 添加 constructor 或类似功能,以便在 builder 函数中直接添加必填参数。
  • 添加 Into 或类似功能以进行类型转换。
  • 自动添加以Some传递选项的方式。
  • 清理

依赖项

~275–730KB
~17K SLoC