You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
patterns/patterns/builder.md

109 lines
3.2 KiB
Markdown

# Builder
## Description
Construct an object with calls to a builder helper.
## Example
```rust
#[derive(Debug, PartialEq)]
pub struct Foo {
// Lots of complicated fields.
bar: String,
}
pub struct FooBuilder {
// Probably lots of optional fields.
bar: String,
}
impl FooBuilder {
pub fn new(/* ... */) -> FooBuilder {
// Set the minimally required fields of Foo.
FooBuilder {
bar: String::from("X"),
}
}
pub fn name(mut self, bar: String) -> FooBuilder {
// Set the name on the builder itself, and return the builder by value.
self.bar = bar;
self
}
// If we can get away with not consuming the Builder here, that is an
// advantage. It means we can use the FooBuilder as a template for constructing
// many Foos.
pub fn build(self) -> Foo {
// Create a Foo from the FooBuilder, applying all settings in FooBuilder
// to Foo.
Foo { bar: self.bar }
}
}
#[test]
fn builder_test() {
let foo = Foo {
bar: String::from("Y"),
};
let foo_from_builder: Foo = FooBuilder::new().name(String::from("Y")).build();
assert_eq!(foo, foo_from_builder);
}
```
## Motivation
Useful when you would otherwise require many different constructors or where
construction has side effects.
## Advantages
Separates methods for building from other methods.
Prevents proliferation of constructors
Can be used for one-liner initialisation as well as more complex construction.
## Disadvantages
More complex than creating a struct object directly, or a simple constructor
function.
## Discussion
This pattern is seen more frequently in Rust (and for simpler objects) than in
many other languages because Rust lacks overloading. Since you can only have a
single method with a given name, having multiple constructors is less nice in
Rust than in C++, Java, or others.
This pattern is often used where the builder object is useful in its own right,
rather than being just a builder. For example, see
[`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html)
is a builder for [`Child`](https://doc.rust-lang.org/std/process/struct.Child.html)
(a process). In these cases, the `T` and `TBuilder` pattern
of naming is not used.
The example takes and returns the builder by value. It is often more ergonomic
(and more efficient) to take and return the builder as a mutable reference. The
borrow checker makes this work naturally. This approach has the advantage that
one can write code like
```rust,ignore
let mut fb = FooBuilder::new();
fb.a();
fb.b();
let f = fb.build();
```
as well as the `FooBuilder::new().a().b().build()` style.
## See also
- [Description in the style guide](https://web.archive.org/web/20210104103100/https://doc.rust-lang.org/1.12.0/style/ownership/builders.html)
- [derive_builder](https://crates.io/crates/derive_builder), a crate for automatically
implementing this pattern while avoiding the boilerplate.
- [Constructor pattern](../idioms/ctor.md) for when construction is simpler.
- [Builder pattern (wikipedia)](https://en.wikipedia.org/wiki/Builder_pattern)
- [Construction of complex values](https://web.archive.org/web/20210104103000/https://rust-lang.github.io/api-guidelines/type-safety.html#c-builder)