Custom Fields
On this page, you'll learn how to add custom fields to the builder type 🌾.
This is useful if you'd like a completely custom state for custom setters in the builder.
To understand how it works, we'll create a builder API similar to std::process::Command
where you have a couple of methods arg
and args
, that push values into an internal arguments Vec
.
We'll use the #[builder(field)]
attribute to define a custom field, that will be accessible in our arg/args
custom setters. The value of this field will be moved into the resulting struct or function from which the builder was generated.
TIP
See Custom Methods for details on how to write an impl block with additional methods for the builder if you haven't already.
use bon::Builder;
#[derive(Builder)]
struct Command {
// Define a private field on the builder without setters.
// It's initialized with `Default::default()` at the start
#[builder(field)]
args: Vec<String>,
#[builder(into)]
name: String,
}
// This is the API we'd like to have
let cmd = Command::builder()
.name("ls")
.arg("-l")
.args(["foo", "bar"])
.build();
assert_eq!(cmd.name, "ls");
assert_eq!(cmd.args, ["-l", "foo", "bar"]);
// Now define custom `arg/args` methods on the builder itself.
impl<S: command_builder::State> CommandBuilder<S> {
fn arg(mut self, arg: impl Into<String>) -> Self {
// We have access to `self.args` private 🔒 field on `CommandBuilder`!
self.args.push(arg.into());
self
}
fn args(mut self, args: impl IntoIterator<Item: Into<String>>) -> Self {
self.args.extend(args.into_iter().map(Into::into));
self
}
}
And that's it! This way you can extend bon
's builders with almost any state and behaviour, that you want.
You can specify a custom initial value with #[builder(field = expr)]
. That expr
can refer to other members and fields defined higher. See the evaluation context reference for details.