with
​
Applies to: struct fields function arguments method arguments
Overrides setters' signature and applies a custom conversion.
You can specify the signature and the conversion either with the closure syntax or with a well-known function.
Example | Meaning |
---|---|
#[builder(with = |...| body)] | Infallible closure |
#[builder(with = |...| -> *Result<_[, E]> { body })] | Fallible closure |
#[builder(with = FromIterator::from_iter)] | Well-known function |
Closure Syntax ​
If you specify a closure, its input parameters will become the input parameters of the setters.
Infallible Closure ​
The simplest form of the custom closure is an infallible closure. It must not have a return type annotation and it must return the value of the underlying member's type.
use bon::Builder;
struct Point {
x: u32,
y: u32,
}
#[derive(Builder)]
struct Example {
#[builder(with = |x: u32, y: u32| Point { x, y })]
point: Point,
}
let value = Example::builder()
.point(2, 3)
.build();
assert_eq!(value.point.x, 2);
assert_eq!(value.point.y, 3);
use bon::builder;
struct Point {
x: u32,
y: u32,
}
#[builder]
fn example(
#[builder(with = |x: u32, y: u32| Point { x, y })]
point: Point,
) -> Point {
point
}
let value = example()
.point(2, 3)
.call();
assert_eq!(value.x, 2);
assert_eq!(value.y, 3);
use bon::bon;
struct Point {
x: u32,
y: u32,
}
struct Example;
#[bon]
impl Example {
#[builder]
fn example(
#[builder(with = |x: u32, y: u32| Point { x, y })]
point: Point,
) -> Point {
point
}
}
let value = Example::example()
.point(2, 3)
.call();
assert_eq!(value.x, 2);
assert_eq!(value.y, 3);
Fallible Closure ​
You can add a return type annotation to the closure to signify that it's fallible. The closure is expected to return a Result
with the Ok
variant of the member's underlying type. This will make the setter fallible.
use bon::Builder;
use std::num::ParseIntError;
#[derive(Builder)]
struct Example {
#[builder(with = |string: &str| -> Result<_, ParseIntError> {
string.parse()
})]
x1: u32,
}
fn main() -> Result<(), ParseIntError> {
Example::builder()
.x1("99")? // <-- the setter returns a `Result`
.build();
Ok(())
}
use bon::builder;
use std::num::ParseIntError;
#[builder]
fn example(
#[builder(with = |string: &str| -> Result<_, ParseIntError> {
string.parse()
})]
x1: u32,
) -> u32 {
x1
}
fn main() -> Result<(), ParseIntError> {
example()
.x1("99")? // <-- the setter returns a `Result`
.call();
Ok(())
}
use bon::bon;
use std::num::ParseIntError;
struct Example;
#[bon]
impl Example {
#[builder]
fn example(
#[builder(with = |string: &str| -> Result<_, ParseIntError> {
string.parse()
})]
x1: u32,
) -> u32 {
x1
}
}
fn main() -> Result<(), ParseIntError> {
Example::example()
.x1("99")? // <-- the setter returns a `Result`
.call();
Ok(())
}
The return type annotation must be of the form *Result<_[, E]>
.
Here *Result
means the type must have a Result
suffix. [, E]
means the error type annotation is optional.
Examples of valid return types:
Result<_, MyError>
Result<_>
my_crate::Result<_>
ApiResult<_>
The symbol _
must be specified verbatim. You don't need to repeat the underlying type of the member there.
Generics ​
You can reference generic parameters defined on the underlying struct
, fn
or the surrounding impl
block. You can also use impl Trait
for parameters in the closure, even though you can't in regular Rust:
#[builder(with = |value: impl Trait| /**/)]
You can't declare new generic parameters. If impl Trait
isn't enough for you, consider defining a custom method on the builder.
Optional members ​
For members of type Option<T>
without #[builder(required)]
, the closure needs to return a value of type T
or Result<T>
.
The maybe_
setter's input type depends on the number of input parameters in the closure:
- If there is a single input parameter of type
T
, then themaybe_
setter acceptsOption<T>
- If there are two or more input parameters of types
T1, T2, ...
, then the maybe setter acceptsOption<(T1, T2, ...)>
use bon::Builder;
#[derive(Builder)]
struct Example {
#[builder(with = |value: u32| value * 2)]
x1: Option<u32>,
#[builder(with = |a: u32, b: u32| a * b)]
x2: Option<u32>,
}
let value = Example::builder()
.maybe_x1(Some(2))
.maybe_x2(Some((2, 3)))
.build();
assert_eq!(value.x1, Some(4));
assert_eq!(value.x2, Some(6));
Well-Known Functions ​
There are several well-known functions that you can specify instead of the closure to shorten your code. All of them have an equivalent closure syntax, so they are just pure syntax sugar.
FromIterator::from_iter
​
Equivalent closure syntax:
#[builder(with = |iter: impl IntoIterator<...>| FromIterator::from_iter(iter))]
#[builder(with = |iter: impl IntoIterator<...>| iter.into_iter().collect())]
Makes the setter accept impl IntoIterator<Item = T>
or impl IntoIterator<Item = (K, V)>
.
This attribute can be used with custom and 3-rd party collections such as indexmap::IndexMap
, not only with the std::collections
. The only requirement is for the collection type to implement the FromIterator
trait and match the following naming patterns:
*Vec<T, ...>
*Set<T, ...>
*Map<K, V, ...>
*Deque<T, ...>
*Heap<T, ...>
*List<T, ...>
The ...
means there may be any number of other generic parameters (including zero), which could be used to pass a custom hasher or allocator.
This attribute can be specified using one of the two equivalent forms:
#[builder(with = FromIterator::from_iter)]
#[builder(with = <_>::from_iter)]
General Rust tip
<_>::method()
is valid Rust syntax, although not everyone knows about it 😉.
For example, if you write Default::default()
in your regular Rust code, then try writing <_>::default()
instead, it'll compile. This notation asks the compiler to infer the trait for the method. Importantly, this notation only works with trait methods. You can't use this syntax for inherent methods.
use bon::Builder;
use std::collections::BTreeMap;
#[derive(Builder)]
struct Example {
#[builder(with = FromIterator::from_iter)]
x1: Vec<u32>,
#[builder(with = <_>::from_iter)]
x2: BTreeMap<u32, u32>
}
Example::builder()
// Accepts `impl IntoIterator<Item = u32>`
.x1([1, 2, 3])
// Accepts `impl IntoIterator<Item = (u32, u32)>`
.x2([
(1, 2),
(3, 4),
])
.build();
use bon::builder;
use std::collections::BTreeMap;
#[builder]
fn example(
#[builder(with = FromIterator::from_iter)]
x1: Vec<u32>,
#[builder(with = <_>::from_iter)]
x2: BTreeMap<u32, u32>
) {}
example()
// Accepts `impl IntoIterator<Item = u32>`
.x1([1, 2, 3])
// Accepts `impl IntoIterator<Item = (u32, u32)>`
.x2([
(1, 2),
(3, 4),
])
.call();
use bon::bon;
use std::collections::BTreeMap;
struct Example;
#[bon]
impl Example {
#[builder]
fn example(
#[builder(with = FromIterator::from_iter)]
x1: Vec<u32>,
#[builder(with = <_>::from_iter)]
x2: BTreeMap<u32, u32>
) {}
}
Example::example()
// Accepts `impl IntoIterator<Item = u32>`
.x1([1, 2, 3])
// Accepts `impl IntoIterator<Item = (u32, u32)>`
.x2([
(1, 2),
(3, 4),
])
.call();
Some
​
Equivalent closure syntax:
#[builder(with = |value: T| Some(value))]
Makes the setter accept the value of type T
assuming the member is of type Option<T>
with #[builder(required)]
.
use bon::Builder;
#[derive(Builder)]
struct Example {
#[builder(required, with = Some)]
x1: Option<u32>,
}
Example::builder()
.x1(1)
.build();
History
This attribute was added as a way to make setters required for structs generated by prost
from the proto3
syntax. Protobuf v3 doesn't take apart required and optional fields, so prost
generates Option<T>
even for required fields (original issue comment). However, you can use #[builder(required, with = Some)]
to mark the fields required in the builder syntax.