How to write an idiomatic build pattern with chained method calls in Rust?

自古美人都是妖i 提交于 2019-12-20 05:49:35

问题


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 accept self 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!