#[derive(Builder)]
/ #[builder]
You can generate a builder using three different kinds of syntax (struct, free function, associated method). They all share two common groups of attributes.
- Top-level attributes - placed on a
struct
orfn
declaration. - Member attributes - placed on a
struct
field orfn
argument.
See examples. Make sure to click through the tabs:
use bon::Builder;
#[derive(Builder)]
#[builder(finish_fn = finish)] // <-- this is a top-level attribute
struct Example {
#[builder(default)] // <-- this is a member attribute
field: u32
}
use bon::builder;
#[builder(finish_fn = finish)] // <-- this is a top-level attribute
fn example(
#[builder(default)] // <-- this is a member attribute
arg: u32
) { }
use bon::bon;
struct Example;
#[bon]
impl Example {
#[builder(finish_fn = finish)] // <-- this is a top-level attribute
fn example(
#[builder(default)] // <-- this is a member attribute
arg: u32
) { }
}
Most of the attributes apply to all kinds of syntax. However, some of them are only available with structs or only with functions/methods, for example. The "Applies to" clause specifies the contexts where the attribute can be used.
Historical note
#[derive(bon::Builder)]
syntax appeared with the version 2.2
of bon
. The older versions of bon
(i.e. <= 2.1
) supported only #[bon::builder]
syntax with structs, but that syntax was deprecated in favor of the derive
syntax for various reasons described in the 2.2 release blog post.
Top-level attributes
builder_type
Applies to: structs free functions associated methods
Overrides the name of the generated builder struct.
The default naming pattern is the following:
Underlying item | Naming pattern |
---|---|
Struct | {StructName}Builder |
StructName::new() method | {StructName}Builder |
Free function | {PascalCaseFunctionName}Builder |
Associated method | {SelfTypeName}{PascalCaseMethodName}Builder |
The attribute expects the desired builder type identifier as its input.
Example:
use bon::Builder;
#[derive(Builder)]
#[builder(builder_type = MyBuilder)]
struct Brush {}
let builder: MyBuilder = Brush::builder();
use bon::builder;
#[builder(builder_type = MyBuilder)]
fn brush() {}
let builder: MyBuilder = brush();
use bon::bon;
struct Brush;
#[bon]
impl Brush {
#[builder(builder_type = MyBuilder)]
fn new() -> Self {
Self
}
}
let builder: MyBuilder = Brush::builder();
You'll usually want to override the builder type name when you already have such a name in scope. For example, if you have a struct and a function with the same name annotated with #[builder]
:
use bon::{builder, Builder};
#[derive(Builder)]
struct Brush {}
#[builder]
fn brush() {}
// `BrushBuilder` builder type name was generated for both
// the struct and the function. This is a compile error
let builder: BrushBuilder = Brush::builder();
let builder: BrushBuilder = brush();
use bon::{builder, Builder};
#[derive(Builder)]
#[builder(builder_type = MyBuilder)]
struct Brush {}
#[builder]
fn brush() {}
// Now builder types are named differently
let builder: MyBuilder = Brush::builder();
let builder: BrushBuilder = brush();
derive
Applies to: structs free functions associated methods
Generates additional derives on the builder type. The syntax is similar to the regular #[derive(...)]
attribute. You need to specify one or more of the supported derives separated by commas.
The following derives are supported: Clone
, Debug
.
WARNING
The format of the Debug
output of the builder is not stable and it may change between the patch versions of bon
.
Example:
use bon::Builder;
#[derive(Builder)]
#[builder(derive(Clone, Debug))]
struct Example {
name: String,
is_admin: bool,
level: Option<u32>,
}
let builder = Example::builder().name("Bon".to_owned());
// We can clone the builder
let builder = builder.clone();
// We can debug-format the builder
let builder_debug = format!("{builder:?}");
assert_eq!(
builder_debug,
// Only the fields that were set will be output
r#"ExampleBuilder { name: "Bon" }"#
);
// Finish building
let example = builder.is_admin(true).build();
use bon::builder;
#[builder(derive(Clone, Debug))]
fn example(
name: String,
is_admin: bool,
level: Option<u32>,
) {}
let builder = example().name("Bon".to_owned());
// We can clone the builder
let builder = builder.clone();
// We can debug-format the builder
let builder_debug = format!("{builder:?}");
assert_eq!(
builder_debug,
// Only the fields that were set will be output
r#"ExampleBuilder { name: "Bon" }"#
);
// Finish building
builder.is_admin(true).call();
use bon::bon;
#[derive(Debug)]
struct Example;
#[bon]
impl Example {
#[builder(derive(Clone, Debug))]
fn method(
name: String,
is_admin: bool,
level: Option<u32>,
) {}
#[builder(derive(Debug))]
fn method_with_self(&self) {}
}
let builder = Example::method().name("Bon".to_owned());
// We can clone the builder
let builder = builder.clone();
// We can debug-format the builder
let builder_debug = format!("{builder:?}");
assert_eq!(
builder_debug,
// Only the fields that were set will be output
r#"ExampleMethodBuilder { name: "Bon" }"#
);
// Finish building
builder.is_admin(true).call();
// The debug output of the builder for methods with `self` includes
// the special `self` field with the receiver.
assert_eq!(
format!("{:?}", Example.method_with_self()),
"ExampleMethodWithSelfBuilder { self: Example }"
)
Compile errors
Requires that all members of the builder including the receiver (if this is a builder for an associated method) implement the target trait. For example, this doesn't compile because not all members implement Clone
:
Example:
use bon::Builder;
struct NonClone;
#[derive(Builder)]
#[builder(Clone)]
struct Example {
// Doesn't derive `Clone`, so this code doesn't compile
non_clone NonClone,
cloneable: u32
}
expose_positional_fn
Applies to: free functions associated methods
When generating builder code for functions the #[builder]
macro hides the original function with positional parameters used to define the builder. That function is invoked inside of the builder's call()
or build()
method.
Usually you'd want the underlying positional function to be hidden to provide only the builder syntax to the callers. However, in some situations you may want to keep the positional function exposed along with the builder syntax for compatibility with old code that still uses the old positional function call syntax.
This attribute can take several forms.
- Simple:
#[builder(expose_positional_fn = identifier)]
. Sets only the name of the positional function. - Verbose:
#[builder(expose_positional_fn(name = identifier, vis = "visibility"))]
. Allows setting both the name and the visibility of the positional function. Each key is optional. Thevis
must be specified as a string literal e.g."pub(crate)"
,"pub"
or""
(empty string means private visibility).
If vis
parameter is not specified, then the visibility of the exposed positional function will be the same as specified on the function that the #[builder]
was applied to.
Example:
use bon::builder;
#[builder(expose_positional_fn = example_positional)]
fn example(x: u32, y: u32) {}
// Positional function is now available under the given name
example_positional(1, 2);
// Builder syntax is also available (unchanged)
example()
.x(1)
.y(2)
.call();
use bon::bon;
struct Example;
#[bon]
impl Example {
#[builder(expose_positional_fn = example_positional)]
fn example(x: u32, y: u32) {}
}
// Positional function is now available under the given name
Example::example_positional(1, 2);
// Builder syntax is also available (unchanged)
Example::example()
.x(1)
.y(2)
.call();
new
method special case
There are two conventional names in Rust ecosystem for constructors and builders:
new
is used for a constructor method that uses positional parametersbuilder
is used for a method that returns a builder for a type
So when #[builder]
is placed on a method called new
, it'll generate a method called builder
that starts the building process. This means there is already a default obvious name for the positional function that expose_positional_fn
may use in this case if you don't specify any value for this attribute.
Example:
use bon::bon;
struct Example {
x: u32,
y: u32,
}
#[bon]
impl Example {
#[builder(expose_positional_fn)]
fn new(x: u32, y: u32) -> Self {
Self { x, y }
}
}
// Positional function is available under the name `new`
Example::new(1, 2);
// Builder syntax is also available (unchanged)
Example::builder()
.x(1)
.y(2)
.build();
This makes it possible to add builder syntax to your existing types that have the new
method without breaking compatibility with old code. Old code can still use T::new()
syntax, while new code can benefit from T::builder()
syntax.
finish_fn
Applies to: structs free functions associated methods
This attribute allows overriding the name of the generated builder's method that finishes the building process.
Example:
use bon::Builder;
#[derive(Builder)]
#[builder(finish_fn = assemble)]
struct Article {
id: u32
}
let article = Article::builder()
.id(42)
.assemble();
assert_eq!(article.id, 42);
use bon::builder;
#[builder(finish_fn = send)]
fn get_article(id: u32) -> String {
format!("Some article with id {id}")
}
let response = get_article()
.id(42)
.send();
assert_eq!(response, "Some article with id 42");
use bon::bon;
struct ArticlesClient;
#[bon]
impl ArticlesClient {
#[builder(finish_fn = send)]
fn get_article(&self, id: u32) -> String {
format!("Some article with id {id}")
}
}
let response = ArticlesClient
.get_article()
.id(42)
.send();
assert_eq!(response, "Some article with id 42");
on
Applies to: structs free functions associated methods
Applies the given builder attributes to all members that match the selected type pattern. For example, you can automatically apply #[builder(into)]
to all members of type String
this way:
use bon::Builder;
#[derive(Builder)]
#[builder(on(String, into))]
struct Example {
id: String,
name: String,
level: u32,
}
Example::builder()
// `id` and `name` accept `impl Into<String>` because
// `on` automatically added `#[builder(into)]` for them
.id("e-1111")
.name("Bon")
// `u32` doesn't match the `String` type pattern,
// so `#[builder(into)]` was not applied to it
.level(100)
.build();
use bon::builder;
#[builder(on(String, into))]
fn example(
id: String,
name: String,
level: u32,
) {}
example()
// `id` and `name` accept `impl Into<String>` because
// `on` automatically added `#[builder(into)]` for them
.id("e-1111")
.name("Bon")
// `u32` doesn't match the `String` type pattern,
// so `#[builder(into)]` was not applied to it
.level(100)
.call();
use bon::bon;
struct Example;
#[bon]
impl Example {
#[builder(on(String, into))]
fn example(
id: String,
name: String,
level: u32,
) {}
}
Example::example()
// `id` and `name` accept `impl Into<String>` because
// `on` automatically added `#[builder(into)]` for them
.id("e-1111")
.name("Bon")
// `u32` doesn't match the `String` type pattern,
// so `#[builder(into)]` was not applied to it
.level(100)
.call();
This attribute must be of form on(type_pattern, attributes)
.
type_pattern
- type that will be compared with the types of the members. The types are compared textually. For example,String
doesn't matchstd::string::String
. You can use_
to mark parts of the type to ignore when matching. For example,Vec<_>
matchesVec<u32>
orVec<String>
. Lifetimes are ignored during matching.attributes
- for now, the only attribute supported in theattributes
position isinto
. It sets#[builder(into)]
for members that match thetype_pattern
.
If you want to apply the attributes
to all members, you can use the _
type pattern that matches any type. For example, #[builder(on(_, into))]
.
For optional members the underlying type is matched ignoring the Option
wrapper.
Example:
use bon::Builder;
#[derive(Builder)]
#[builder(on(String, into))]
struct Example {
name: String,
description: Option<String>,
#[builder(default)]
alias: String
}
Example::builder()
.name("Bon")
// These members also match the `String` type pattern,
// so `#[builder(into)]` was applied to them
.description("accepts an `impl Into<String>` here")
.alias("builder")
.build();
You can specify on(...)
multiple times.
Example:
use bon::Builder;
use std::path::PathBuf;
#[derive(Builder)]
#[builder(on(String, into), on(PathBuf, into))]
struct Example {
name: String,
path: PathBuf,
level: u32,
}
Example::builder()
.name("accepts `impl Into<String>`")
.path("accepts/impl/into/PathBuf")
// This member doesn't match either `String` or `PathBuf`,
// and thus #[builder(into)] was not applied to it
.level(100)
.build();
start_fn
Applies to: structs
Overrides the name and visibility of the associated method that starts the building process, i.e. returns the builder for the struct.
The default name for this method is builder
, and the default visibility is the same as the visibility of the struct itself.
This attribute can take several forms.
- Simple:
#[builder(start_fn = identifier)]
. Overrides only the name of the "start" method. - Verbose:
#[builder(start_fn(name = identifier, vis = "visibility"))]
. Allows overriding both the name and the visibility of the "start" method. Each key is optional. Thevis
must be specified as a string literal e.g."pub(crate)"
,"pub"
or""
(empty string means private visibility).
Example:
use bon::Builder;
#[derive(Builder)]
#[builder(start_fn = init)]
struct User {
id: u32
}
User::init()
.id(42)
.build();
use bon::Builder;
// `User::init()` method will have `pub(crate)` visibility
// Use `vis = ""` to make it fully private instead
#[derive(Builder)]
#[builder(start_fn(name = init, vis = "pub(crate)"))]
pub struct User {
id: u32
}
User::init()
.id(42)
.build();
Member attributes
default
Applies to: struct fields free function arguments associated method arguments
Makes the member optional and assigns a default value to it. There will be two setter methods generated for the member just like for members of type Option<T>
. One setter accepts a value of type T
(type of the member) and the other (with the maybe_
prefix) accepts an Option<T>
.
TIP
Switching between #[builder(default)]
and Option<T>
is compatible.
The default value will be lazily computed inside of the finishing function (i.e. build()
or call()
). It is computed only if the setter for the member wasn't called or None
was passed to the maybe_{member}()
setter.
The default value is computed based on the form of this attribute:
Form | How default value is computed |
---|---|
#[builder(default)] | Default::default() |
#[builder(default = expression)] | expression |
The result of the expression
will be converted into the target type using Into::into
if #[builder(into)]
is enabled for the setter.
Example:
use bon::Builder;
#[derive(Builder)]
struct User {
#[builder(default)]
level: u32,
// The expression of type `&'static str` is automatically
// converted to `String` here via `Into` thanks to `#[builder(into)].
#[builder(into, default = "anon")]
name: String,
// Any complex expression is accepted
#[builder(default = bon::vec!["read"])]
permissions: Vec<String>,
}
let user = User::builder().build();
assert_eq!(user.level, 0);
assert_eq!(user.name, "anon");
assert_eq!(user.permissions, ["read"]);
use bon::builder;
#[builder]
fn greet_user(
#[builder(default)]
level: u32,
// The expression of type `&'static str` is automatically
// converted to `String` here via `Into` thanks to `#[builder(into)].
#[builder(into, default = "anon")]
name: String,
// Any complex expression is accepted
#[builder(default = bon::vec!["read"])]
permissions: Vec<String>,
) -> String {
format!("Hello {name}! Your level is {level}, permissions: {permissions:?}")
}
let greeting = greet_user().call();
assert_eq!(greeting, "Hello anon! Your level is 0, permissions: [\"read\"]");
use bon::bon;
struct User {
level: u32,
name: String,
permissions: Vec<String>,
}
#[bon]
impl User {
#[builder]
fn new(
#[builder(default)]
level: u32,
// The expression of type `&'static str` is automatically
// converted to `String` here via `Into` thanks to `#[builder(into)].
#[builder(into, default = "anon")]
name: String,
// Any complex expression is accepted
#[builder(default = bon::vec!["read"])]
permissions: Vec<String>,
) -> Self {
Self { level, name, permissions }
}
}
let user = User::builder().build();
assert_eq!(user.name, "anon");
assert_eq!(user.level, 0);
assert_eq!(user.permissions, ["read"]);
You can also use the values of other members by referencing their names in the default
expression. All members are initialized in the order of their declaration. It means only those members that are declared earlier (higher) in the code are available to the default
expression.
Example:
use bon::Builder;
#[derive(Builder)]
struct Example {
member_1: u32,
// Note that here we don't have access to `member_3`
// because it's declared (and thus initialized) later
#[builder(default = 2 * member_1)]
member_2: u32,
#[builder(default = member_2 + member_1)]
member_3: u32,
}
let example = Example::builder()
.member_1(3)
.build();
assert_eq!(example.member_1, 3);
assert_eq!(example.member_2, 6);
assert_eq!(example.member_3, 9);
use bon::builder;
#[builder]
fn example(
member_1: u32,
// Note that here we don't have access to `member_3`
// because it's declared (and thus initialized) later
#[builder(default = 2 * member_1)]
member_2: u32,
#[builder(default = member_2 + member_1)]
member_3: u32,
) -> (u32, u32, u32) {
(member_1, member_2, member_3)
}
let example = example()
.member_1(3)
.call();
assert_eq!(example, (3, 6, 9));
use bon::bon;
struct Example;
#[bon]
impl Example {
#[builder]
fn example(
member_1: u32,
// Note that here we don't have access to `member_3`
// because it's declared (and thus initialized) later
#[builder(default = 2 * member_1)]
member_2: u32,
#[builder(default = member_2 + member_1)]
member_3: u32,
) -> (u32, u32, u32) {
(member_1, member_2, member_3)
}
}
let example = Example::example()
.member_1(3)
.call();
assert_eq!(example, (3, 6, 9));
Caveats
The self
parameter in associated methods is not available to the default
expression. If you need the self
context for your defaulting logic, then set your member's type to Option<T>
and handle the defaulting in the function's body manually.
Compile errors
This attribute is incompatible with members of Option
type, since Option
already implies the default value of None
.
finish_fn
Applies to: struct fields free function arguments associated method arguments
Makes the member a positional argument on the finishing function that consumes the builder and returns the resulting object (for struct syntax) or performs the requested action (for function/method syntax).
The ordering of members annotated with #[builder(finish_fn)]
matters! They will appear in the same order relative to each other in the finishing function signature. They must also be declared at the top of the members list strictly after members annotated with #[builder(start_fn)]
(if any).
This ensures a consistent initialization order, and it makes these members available for expressions in #[builder(default/skip = ...)]
for regular members that follow them.
TIP
Don't confuse this with the top-level #[builder(finish_fn = ...)]
attribute that can be used to configure the name and visibility of the finishing function. You'll likely want to use it in combination with this member-level attribute to define a better name for the finishing function.
Example:
use bon::Builder;
#[derive(Builder)]
// Top-level attribute to give a better name for the finishing function
#[builder(finish_fn = sign)]
struct Message {
// Member-level attribute to mark the member as a parameter of `sign()`
#[builder(finish_fn)]
author_first_name: String,
#[builder(finish_fn)]
author_last_name: String,
payload: String,
}
let message = Message::builder()
.payload("Bon is great! Give it a ⭐".to_owned())
.sign("Sweetie".to_owned(), "Drops".to_owned());
assert_eq!(message.payload, "Bon is great! Give it a ⭐");
assert_eq!(message.author_first_name, "Sweetie");
assert_eq!(message.author_last_name, "Drops");
use bon::builder;
// Top-level attribute to give a better name for the finishing function
#[builder(finish_fn = send)]
fn message(
// Member-level attribute to mark the member as a parameter of `sign()`
#[builder(finish_fn)]
receiver_first_name: String,
#[builder(finish_fn)]
receiver_last_name: String,
payload: String,
) {}
message()
.payload("Bon is great! Give it a ⭐".to_owned())
.send("Sweetie".to_owned(), "Drops".to_owned());
use bon::bon;
struct Chat {}
#[bon]
impl Chat {
// Top-level attribute to give a better name for the finishing function
#[builder(finish_fn = send)]
fn message(
&self,
// Member-level attribute to mark the member as a parameter of `sign()`
#[builder(finish_fn)]
receiver_first_name: String,
#[builder(finish_fn)]
receiver_last_name: String,
payload: String,
) {}
}
let chat = Chat {};
chat.message()
.payload("Bon is great! Give it a ⭐".to_owned())
.send("Sweetie".to_owned(), "Drops".to_owned());
You can also combine this attribute with #[builder(into)]
or #[builder(on(..., into))]
to add an into conversion for the parameter.
Importantly, Into
conversions for such members work slightly differently from the regular (named) members in regard to the Option
type. The Option
type gives no additional meaning to the member annotated with #[builder(finish_fn)]
. Thus, it is matched by the type pattern of on(..., into)
and wrapped with impl Into<Option<T>>
as any other type.
TIP
In general, it's not recommended to annotate optional members with #[builder(finish_fn)]
because you can't omit setting them using the positional function call syntax.
into
Applies to: struct fields free function arguments associated method arguments
Changes the signature of the generated setters to accept impl Into<T>
, where T
is the type of the member.
For optional members, the maybe_{member}()
setter method will accept an Option<impl Into<T>>
type instead of just Option<T>
.
For members that use #[builder(default = expression)]
, the expression
will be converted with Into::into
.
This parameter is often used with the String
type, which allows you to pass &str
into the setter without calling .to_owned()
or .to_string()
on it.
See the "Into Conversions In-Depth" page that shows the common patterns and antipatterns of impl Into<T>
.
Example:
use bon::Builder;
#[derive(Builder)]
struct Example {
#[builder(into)]
name: String,
#[builder(into)]
description: Option<String>,
// The value passed to `default = ...` is converted with `into` as well
#[builder(into, default = "anon")]
group: String
}
Example::builder()
// We can pass `&str` because the setters accept `impl Into<String>`
.name("Bon")
.description("Awesome crate 🐱. Consider giving it a star on Github ⭐")
// We can pass `Option<&str>` to `maybe_` methods because they accept
// `Option<impl Into<String>>`
.maybe_group(Some("Favourites"))
.build();
use bon::builder;
#[builder]
fn example(
#[builder(into)]
name: String,
#[builder(into)]
description: Option<String>,
// The value passed to `default = ...` is converted with `into` as well
#[builder(into, default = "anon")]
group: String
) {}
example()
// We can pass `&str` because the setters accept `impl Into<String>`
.name("Bon")
.description("Awesome crate 🐱. Consider giving it a star on Github ⭐")
// We can pass `Option<&str>` to `maybe_` methods because they accept
// `Option<impl Into<String>>`
.maybe_group(Some("Favourites"))
.call();
use bon::bon;
struct Example;
#[bon]
impl Example {
#[builder]
fn example(
#[builder(into)]
name: String,
#[builder(into)]
description: Option<String>,
// The value passed to `default = ...` is converted with `into` as well
#[builder(into, default = "anon")]
group: String
) {}
}
Example::example()
// We can pass `&str` because the setters accept `impl Into<String>`
.name("Bon")
.description("Awesome crate 🐱. Consider giving it a star on Github ⭐")
// We can pass `Option<&str>` to `maybe_` methods because they accept
// `Option<impl Into<String>>`
.maybe_group(Some("Favourites"))
.call();
name
Applies to: struct fields free function arguments associated method arguments
Overrides the name of the member in the builder's setters and type state. This is most useful when with struct syntax (#[derive(Builder)]
) where you'd like to use a different name for the field internally. For functions this attribute makes less sense since it's easy to just create a variable named differently let new_name = param_name;
. However, this attribute is still supported on function arguments.
Example:
use bon::Builder;
#[derive(Builder)]
struct Player {
#[builder(name = rank)]
level: u32
}
Player::builder()
.rank(10)
.build();
use bon::builder;
#[builder]
fn player(
#[builder(name = rank)]
level: u32
) {}
player()
.rank(10)
.call();
use bon::bon;
struct Player {
level: u32,
}
#[bon]
impl Player {
#[builder]
fn new(
#[builder(name = rank)]
level: u32
) -> Self {
Self { level }
}
}
Player::builder()
.rank(10)
.build();
skip
Applies to: struct fields
Skips generating setters for the member. This hides the member from the generated builder API, so the caller can't set its value.
The value for the member will be computed based on the form of the attribute:
Form | How value for the member is computed |
---|---|
#[builder(skip)] | Default::default() |
#[builder(skip = expression)] | expression |
Example:
use bon::Builder;
#[derive(Builder)]
struct User {
#[builder(skip)]
level: u32,
// Any complex expression is accepted
#[builder(skip = "anon".to_owned())]
name: String,
}
let user = User::builder()
// There are no `level`, and `name` setters generated
.build();
assert_eq!(user.level, 0);
assert_eq!(user.name, "anon");
You can also use the values of other members by referencing their names in the skip
expression. All members are initialized in the order of their declaration. It means only those members that are declared earlier (higher) in the code are available to the skip
expression.
Example:
use bon::Builder;
#[derive(Builder)]
struct Example {
member_1: u32,
// Note that here we don't have access to `member_3`
// because it's declared (and thus initialized) later
#[builder(skip = 2 * member_1)]
member_2: u32,
#[builder(skip = member_2 + member_1)]
member_3: u32,
}
let example = Example::builder()
.member_1(3)
.build();
assert_eq!(example.member_1, 3);
assert_eq!(example.member_2, 6);
assert_eq!(example.member_3, 9);
This attribute is not supported with free function arguments or associated method arguments because it's simply unnecessary there and can easier be expressed with local variables.
start_fn
Applies to: struct fields free function arguments associated method arguments
Makes the member a positional argument on the starting function that creates the builder.
The ordering of members annotated with #[builder(start_fn)]
matters! They will appear in the same order relative to each other in the starting function signature. They must also be declared at the top of the members' list.
This ensures a consistent initialization order, and it makes these members available for expressions in #[builder(default/skip = ...)]
for regular members that follow them.
TIP
Don't confuse this with the top-level #[builder(start_fn = ...)]
attribute that can be used to configure the name and visibility of the starting function. You'll likely want to use it in combination with this member-level attribute to define a better name for the starting function.
Example:
use bon::Builder;
#[derive(Builder)]
// Top-level attribute to give a better name for the starting function
#[builder(start_fn = with_coordinates)]
struct Treasure {
// Member-level attribute to mark the member as
// a parameter of `with_coordinates()`
#[builder(start_fn)]
x: u32,
#[builder(start_fn)]
y: u32,
label: Option<String>,
}
let treasure = Treasure::with_coordinates(2, 9)
.label("knowledge".to_owned())
.build();
assert_eq!(treasure.x, 2);
assert_eq!(treasure.y, 9);
assert_eq!(treasure.label.as_deref(), Some("knowledge"));
use bon::builder;
#[builder]
fn mark_treasure_at(
#[builder(start_fn)]
x: u32,
#[builder(start_fn)]
y: u32,
label: Option<String>,
) {}
mark_treasure_at(2, 9)
.label("knowledge".to_owned())
.call();
use bon::bon;
struct Navigator {}
#[bon]
impl Navigator {
#[builder]
fn mark_treasure_at(
&self,
#[builder(start_fn)]
x: u32,
#[builder(start_fn)]
y: u32,
label: String,
) {}
}
let navigator = Navigator {};
navigator
.mark_treasure_at(2, 9)
.label("knowledge".to_owned())
.call();
You can also combine this attribute with #[builder(into)]
or #[builder(on(..., into))]
to add an into conversion for the parameter.
Importantly, Into
conversions for such members work slightly differently from the regular (named) members in regard to the Option
type. The Option
type gives no additional meaning to the member annotated with #[builder(start_fn)]
. Thus, it is matched by the type pattern of on(..., into)
and wrapped with impl Into<Option<T>>
as any other type.
TIP
In general, it's not recommended to annotate optional members with #[builder(start_fn)]
because you can't omit setting them using the positional function call syntax.