如何用Rust中的链式方法调用编写一个惯用的构建模式?

时间:2017-01-12 15:37:58

标签: rust convention builder-pattern

根据以下示例,可以在Rust中使用链式方法调用编写构建模式,该方法调用通过值传递或通过引用传递(使用生命周期说明符)

Rust中的构建器模式可能如下所示:

 ui::Button::new()
    .label("Test")
    .align(Align::Center)
    .build();

写作惯用语时,是否有一种强烈的偏好?

是否有一些很好的例子说明如何在Rust中写这个?

2 个答案:

答案 0 :(得分:5)

实际上有两个权衡:

  • 命名的setter是否应该按值或引用接受self
  • 最终build方法应该通过值或引用接受self吗?

我的建议是:

  • setter的可变引用
  • build方法的值

这与使用build中的引用的Rust Book中提供的Builder Pattern略有不同。

为什么要通过setter的可变引用传递?

虽然编译器可以优化调用fn label(self, &str) -> ButtonBuilder所引起的移动,但不能保证。

另一方面,可变参考方式已经是最优的,因此您无需依赖优化器。

为什么要通过最终build的值?

对于仅由Copy字段组成的构建器,buildself &self之间没有区别。

但是,只要构建器包含非Copy字段,将&self传递给build就需要深度克隆这些字段。

另一方面,按值self传递build可以移动字段,从而避免不必要的副本。

如果希望重新使用构建器,那么构建器应该实现Clone

答案 1 :(得分:1)

我已经看到构建器模式主要是通过在修改它时获取Builder的所有权来实现的,并且通过build()的引用来实现。例如,

#[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

这使链接变得简单,同时允许重用构建器。