4 个版本 (2 个破坏性版本)
0.3.0 | 2023 年 10 月 3 日 |
---|---|
0.2.0 | 2023 年 9 月 20 日 |
0.1.1 | 2023 年 9 月 13 日 |
0.1.0 | 2023 年 9 月 13 日 |
#25 in #derive-builder
在 2 个 crate 中使用 (通过 const_typed_builder)
98KB
1.5K SLoC
Builder
派生宏文档
Builder
派生宏用于为 Rust 中的结构体生成构建器方法,该库最大的特点是它在编译时对结构体进行验证。用户可以采用几种配置来定义复杂结构体的有效性,并在结构体创建之前进行检查。
库还会检查结构体永远无法有效或始终有效的场景,但这仍在进行中。错误总是会被发出,但警告仅在夜间渠道上发出。这是由于 proc_macro_error 的限制。
先决条件
要使用 Builder
派生宏,你应该在项目的 Cargo.toml
文件中将 const_typed_builder
crate 添加到项目的依赖中
[dependencies]
const_typed_builder = "0.3"
此外,请确保你的代码中有以下使用语句
use const_typed_builder::Builder;
概述
该 crate 可以用于在编译时检查结构体的有效性。用户只能在结构体有效后才能调用构建。这是通过检查所有必填字段是否实例化以及所有用户定义的 "组" 是否有效来完成的。
示例
基本用法
use const_typed_builder::Builder;
#[derive(Builder)]
pub struct Foo {
bar: String,
}
let foo = Foo::builder()
.bar("Hello world!".to_string()) // <- The program would not compile without this call
.build(); // <- Because this function is only implemented for the
// .. version of FooBuilder where `bar` is initialized
launchd 的子集
use const_typed_builder::Builder;
use std::path::PathBuf;
#[derive(Builder)]
pub struct ResourceLimits {
core: Option<u64>,
// ...
}
#[derive(Builder)]
#[group(program = at_least(1))]
pub struct Launchd {
#[builder(mandatory)]
label: Option<String>,
disabled: Option<bool>,
user_name: Option<String>,
group_name: Option<String>,
// ...
#[builder(group = program)]
program: Option<PathBuf>,
bundle_program: Option<String>,
#[builder(group = program)]
program_arguments: Option<Vec<String>>,
// ...
#[builder(skip)]
on_demand: Option<bool>, // NB: deprecated (see KeepAlive), but still needed for reading old plists.
#[builder(skip)]
service_ipc: Option<bool>, // NB: "Please remove this key from your launchd.plist."
// ...
#[builder(propagate)]
soft_resource_limits: Option<ResourceLimits>,
#[builder(propagate)]
hard_resource_limits: Option<ResourceLimits>,
// ...
}
let launchd = Launchd::builder()
.label("my_label".to_string()) // <- 1: Mandatory
.program("./my_program".into()) // <- 2: Launchd expects that least one of these fields is set..
.program_arguments( // <- 2: .. We can remove either one, but never both
vec!["my_arg".to_string()]
)
// .on_demand(false) <- 3: This function doesn't exist
.soft_resource_limits(|builder|
Some(builder.core(Some(1)).build()) // <- 4: Propagating to `ResourceLimits::builder`
)
.build();
属性
这是对该库功能的一个快速概述。有关所有功能的更深入解释,包括示例,请参阅 const_typed_builder_derive::Builder
。 结构体
#[builder(assume_mandatory)]
:表示该结构体中的所有字段都应被视为必填。如果没有提供等号(例如,#[builder(assume_mandatory)]
),则将字段的mandatory
标志设置为true。如果提供等号(例如,#[builder(assume_mandatory = true)]
),则根据值设置字段的mandatory
标志。#[group(group_name = (exact(N)|at_least(N)|at_most(N)|single)]
:将结构体的字段与名为"group_name"的组相关联,并指定组的操作行为。该group_name
应为一个字符串标识符。组可以有以下行为之一:exact(N)
:在构建器构建过程中,组中必须恰好设置N个字段。at_least(N)
:在构建器构建过程中,组中至少需要设置N个字段。at_most(N)
:在构建器构建过程中,组中最多只能设置N个字段。single
:在构建器构建过程中,组中只能设置一个字段。这是exact(1)
的简写。例如,#[group(foo = at_least(2))]
创建了一个至少需要初始化2个字段的组。
#[builder(solver = (brute_force|compiler))]
: 请谨慎使用,参见本文件底部说明! 指定用于构建结构的求解器类型。solve_type
应为预定义的求解器类型之一,例如brute_force
或compiler
。如果提供等号(例如,#[builder(solver = brute_force)]
),则相应设置“求解器类型”。此属性仍在测试中,默认为brute_force
,只有当编译时间出现问题,您可以尝试compiler
。不过,compiler
给出的保证较少。
字段
#[builder(group = group_name)]
: 该库的核心。将字段与名为group_name
的组相关联。同一组中的字段被视为一个单元,在构建器构建过程中至少必须设置其中一个。此属性允许将组名指定为标识符(例如,group = my_group
)或字符串(例如,group = "my_group"
)。#[builder(mandatory)]
: 标记字段为必填,意味着在构建器构建过程中必须设置。如果不提供等号(例如,#[builder(mandatory)]
),则将字段设置为必填。如果提供等号(例如,#[builder(mandatory = true)]
),则根据值设置必填标志。#[builder(optional)]
:将字段标记为可选,这是与#[builder(mandatory)]
完全相反的。如果提供时没有等号(例如,#[builder(optional)]
),则将字段设置为可选。如果提供等号(例如,#[builder(optional = true)]
),则根据值设置可选标志。#[builder(skip)]
:将字段标记为跳过,意味着构建器将不包括它。这可以用于已经弃用但必须反序列化的字段。这样,您可以确保永远不会使用该字段初始化创建新的结构,但旧的结构仍然可以使用。对于此功能,字段类型必须是Option<T>
。#[builder(propagate)]
:表示当构建器构建时,字段应该传播其值。如果存在此属性,则当使用构建器构建对象时,字段的值将被复制或移动到构建的对象中。
字段可以是组的一部分、必填、可选或跳过。这些属性属性是互斥的。propagate
可以用于任何类型也派生自Builder
的字段。
[!注意]检查每个字段的正确性是直接与SAT问题相关,这是一个NP完全问题。这尤其影响了分组字段。当前分组字段正确性检查的默认实现是
brute_force
,当前实现具有$O(2^g)
$的复杂性,其中$g
$是分组变量的数量。这对几个字段来说不是问题,但可能会显著影响编译时间。这仍然可以显著优化。未来的版本可能会改善这种复杂性。另一种实现是
compiler
。我还没有测试其速度提升,但它可能存在一些问题。虽然我还没有能够重现这个问题,但似乎const值并不保证在编译时进行评估。这导致问题在于组验证并不保证在编译时失败。用户可以选择启用
compiler
求解器,通过在结构体上方添加以下代码:#[builder(solver = compiler)]
。我对它的性能不提供任何保证。任何愿意帮助并添加SAT求解器作为依赖(在功能标志之后)的人都可以这样做!
灵感
之前已经实现过构建器宏,但并不完全符合我的使用场景。还可以查看derive_builder和typed-builder。这些项目目前更为成熟,但任何愿意测试这个crate的人都是一大帮助。
依赖项
~1.3–1.9MB
~35K SLoC