bon
is a Rust crate for generating compile-time-checked builders for functions and structs. It also provides idiomatic partial application with optional and named parameters for functions and methods.
If you don't know about bon
, then see the motivational blog post and the crate overview.
Meme of this release 🐱
New features
Positional arguments in starting and finishing functions
While having the ability to use separate setters for the members gives you a ton of flexibility and extensibility described on the "Compatibility" page, sometimes you don't need all of that.
Maybe you'd like to pick out some specific members and let the user pass their values as positional parameters to the starting function that creates the builder or to the finishing function that consumes it. This reduces the syntax a bit at the cost of some extensibility loss ⚖️, but it may be worth it!
Starting function
As an example, suppose we have a Treasure
struct with x
and y
coordinates and a label
that describes the payload of the treasure. Since all treasures are located somewhere, they all have coordinates, and it would be cool to specify them in a single starting function call.
To do that we can use the #[builder(start_fn)]
attribute. There are two contexts where we can place it, and they both have a different meaning:
- Top-level
#[builder(start_fn = ...)]
- configures the name of the starting function and optionally its visibility - Member-level
#[builder(start_fn)]
- configures the member to be a positional parameter on the starting function
We'll want to use both of these attributes in our example to give a better name for the starting function that describes its inputs and configure x
and y
as positional parameters on the starting function as well.
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("oats".to_owned())
.build();
assert_eq!(treasure.x, 2);
assert_eq!(treasure.y, 9);
assert_eq!(treasure.label.as_deref(), Some("oats"));
Here, the generated with_coordinates
method has the following signature:
impl Treasure {
fn with_coordinates(x: u32, y: u32) -> TreasureBuilder { /**/ }
}
Finishing function
Now let's say we need to know the person who claimed the Treasure
. While describing the treasure using the current builder syntax we'd like the person who claimed it to specify their first name and last name at the end of the building process.
We can use a similar combination of the top-level #[builder(finish_fn = ...)]
and the member-level #[builder(finish_fn)]
attributes to do that.
Example:
use bon::Builder;
#[derive(Builder)]
#[builder(start_fn = with_coordinates)]
#[builder(finish_fn = claim)]
struct Treasure {
#[builder(start_fn)]
x: u32,
#[builder(start_fn)]
y: u32,
#[builder(finish_fn)]
claimed_by_first_name: String,
#[builder(finish_fn)]
claimed_by_last_name: String,
label: Option<String>,
}
let treasure = Treasure::with_coordinates(2, 9)
.label("oats".to_owned())
.claim("Lyra".to_owned(), "Heartstrings".to_owned());
assert_eq!(treasure.x, 2);
assert_eq!(treasure.y, 9);
assert_eq!(treasure.label.as_deref(), Some("oats"));
assert_eq!(treasure.claimed_by_first_name, "Lyra");
assert_eq!(treasure.claimed_by_last_name, "Heartstrings");
You may also combine these attributes with #[builder(into)]
or #[builder(on(..., into))]
to reduce the number of to_owned()
calls a bit. See this described in detail on the new "Positional members" page in the guide.
Guaranteed MSRV is 1.59.0 now
On the previous week's update (2.2 release) a promise was made to reduce the MSRV (minimum supported Rust version) from the initial 1.70.0 even further, and this has been done 🎉!
This is the lowest possible MSRV we can guarantee for now. The choice of this version was made based on our design requirements for const generics supports described in the comment here.
Deprecation warnings
As was promised in the previous release we are enabling deprecation warnings for the usage of the bare #[bon::builder]
attribute on structs in favour of the new #[derive(bon::Builder)]
syntax.
The #[builder]
syntax is still supported on functions and associated methods, and it's the only way to generate builders for them.
The reasons for this deprecation as well as the instruction to update your code are described in the 2.2. release blog post.
WARNING
This isn't a breaking change, and the code that uses #[bon::builder]
on a struct will still compile albeit with a compiler warning. Once bon
reaches a 3.0
release we'll remove support for #[bon::builder]
on structs entirely. However, there are no particular reasons and plans for a new major release of bon
yet.
Summary
Huge thank you for 925 stars ⭐ on Github! Consider giving bon
a star if you haven't already. Your support and feedback are a big motivation and together we can build a better builder 🐱!
Bon's goal is to empower everyone to build beautiful APIs with great flexibility and extensibility. If you have any feedback or ideas for improvement consider joining our Discord server to discuss them, or just open an issue on Github.