#[derive(Builder)]
automatically implements the builder pattern for arbitrary structs.
We started #[derive(Builder)]
back in August 2016 with the tools available at that time (i.e. macros by example). It was able to derive some setters for a given struct, but within some hard constraints. Luckily that has changed now.
Welcome macros 1.1
With the stabilisation of custom derive in rust 1.15 we are now able to overhaul the derive_builder crate.
The major change is, that we now implement the traditional builder pattern instead of a simplified version. A simple #[derive(Builder)]
will now generate a FooBuilder
for your struct Foo
with all setter-methods and a build method. Previously everything happened on Foo
without a dedicated builder struct.
New features include
- customize builder name, e.g.
#[builder(name="MyBuilder")]
- private setters, e.g.
#[builder(private)]
- prefixes, e.g.
#[builder(setter(prefix="with"))]
- different setter pattern, e.g.
#[builder(pattern="immutable")]
- field specific overrides
- additional debug info via env_logger, e.g.
RUST_LOG=derive_builder=trace cargo test
A quick example
Just copy+paste this to give it a try.
#[macro_use]
extern crate derive_builder;
#[derive(Default, Builder, Debug)]
struct Channel {
token: i32,
// opt into setter type conversion
#[builder(setter(into))]
special_info: i32,
// .. a whole bunch of other fields ..
}
fn main() {
// builder pattern, go, go, go!...
let ch = ChannelBuilder::default()
.token(19124)
.special_info(42u8)
.build()
.unwrap();
println!("{:?}", ch);
}
Note that we did not write any definition or implementation of ChannelBuilder
. Instead the derive_builder crate acts on #[derive(Builder)]
and generates the necessary code at compile time.
show generated code
This is the generated boilerplate code you didn't need to write.
#[derive(Clone, Default)]
struct ChannelBuilder {
token: Option<i32>,
special_info: Option<i32>,
}
#[allow(dead_code)]
impl ChannelBuilder {
pub fn token(&mut self, value: i32) -> &mut Self {
let mut new = self;
new.token = Some(value);
new
}
pub fn special_info<VALUE: Into<i32>>(&mut self, value: VALUE) -> &mut Self {
let mut new = self;
new.special_info = Some(value.into());
new
}
fn build(&self) -> Result<Channel, String> {
Ok(Channel {
token: Clone::clone(self.token
.as_ref()
.ok_or(
"token must be initialized")?),
special_info: Clone::clone(self.special_info
.as_ref()
.ok_or("special_info must be initialized")?),
})
}
}
Get Started
It's as easy as 1+1:
- Add derive_builder to your Cargo.toml either manually or with cargo-edit:
cargo add derive_builder
- Annotate your struct with
#[derive(Builder)]
Further Reading
The documentation covers detailed explanation of all features and tips for troubleshooting. You'll also find a discussion of different builder patterns.
Feedback
We would like to hear your thoughts and ideas about this new crate. Does it help solve your problem? What do you miss?
Credits
Special thanks to @killercup and @jer for their feedback and contributions.