问题
Based on the following examples, its possible to write a build-pattern with chained method calls in Rust which either passes by value or by reference (with a lifetime specifier)
- Is it possible to create a macro to implement builder pattern methods?
- How to overload the 'new' method? (top answer)
- https://github.com/rust-unofficial/patterns/blob/master/patterns/builder.md
A builder pattern in Rust may look something like this:
ui::Button::new()
.label("Test")
.align(Align::Center)
.build();
When writing idiomatic Rust is there a strong preference for one over another?
Is there some good example of how to write this in Rust?
回答1:
There are actually two trade-offs:
- should the named setter accept
self
by value or reference? - should the final
build
method acceptself
by value or reference?
My recommendation is:
- mutable reference for the setters
- value for the
build
method
This differs slightly from the Builder Pattern presented in the Rust Book which uses a reference in build
.
Why passing by mutable reference for the setters?
While a compiler may optimize away the moves caused by a call to fn label(self, &str) -> ButtonBuilder
, it is not guaranteed.
On the other hand, the mutable reference way is already optimal so that you need not rely on the optimizer.
Why passing by value for the final build
?
For builders only composed of Copy
fields, there is no difference between build
taking self
or &self
.
However, as soon as the builder contains non-Copy
fields, passing &self
to build
requires deep-cloning these fields.
On the other hand, passing self
by value allows build
to move the fields, which avoid unnecessary copies.
If one wishes to re-use the builder, then the builder should implement Clone
.
回答2:
I've seen the builder pattern mostly implemented by taking ownership of the Builder when modifying it, and by reference for build()
. For example,
#[derive(Debug, Eq, PartialEq)]
struct Foo {
value: usize,
}
struct FooBuilder {
foos: usize,
bars: usize,
}
impl FooBuilder {
fn new() -> FooBuilder {
FooBuilder {
foos: 0,
bars: 0,
}
}
fn set_foos(mut self, foos: usize) -> FooBuilder {
self.foos = foos;
self
}
fn set_bars(mut self, bars: usize) -> FooBuilder {
self.bars = bars;
self
}
fn build(&self) -> Foo {
Foo {
value: self.foos + self.bars,
}
}
}
fn main() {
let foo = FooBuilder::new()
.set_foos(2)
.set_bars(3)
.build();
assert_eq!(foo, Foo { value: 5 });
}
Try on Rust Playground
This makes chaining simple, while allowing reuse of the builder.
来源:https://stackoverflow.com/questions/41617182/how-to-write-an-idiomatic-build-pattern-with-chained-method-calls-in-rust