【问题标题】:Should I take `self` by value or mutable reference when using the Builder pattern?使用 Builder 模式时,我应该通过值还是可变引用来获取“self”?
【发布时间】:2022-01-21 06:37:03
【问题描述】:

到目前为止,我已经在官方 Rust 代码和其他 crate 中看到了两种构建器模式:

impl DataBuilder {
    pub fn new() -> DataBuilder { ... }
    pub fn arg1(&mut self, arg1: Arg1Type) -> &mut Builder { ... }
    pub fn arg2(&mut self, arg2: Arg2Type) -> &mut Builder { ... }
    ...
    pub fn build(&self) -> Data { ... }
}
impl DataBuilder {
    pub fn new() -> DataBuilder { ... }
    pub fn arg1(self, arg1: Arg1Type) -> Builder { ... }
    pub fn arg2(self, arg2: Arg2Type) -> Builder { ... }
    ...
    pub fn build(self) -> Data { ... }
}

我正在编写一个新的 crate,但我有点困惑我应该选择哪种模式。我知道如果我以后更改一些 API 会很痛苦,所以我想现在就做出决定。

我理解它们之间的语义差异,但在实际情况下我们应该更喜欢哪一个?或者我们应该如何选择它们?为什么?

【问题讨论】:

标签: design-patterns rust ownership


【解决方案1】:

从同一个构建器构建多个值是否有益?

  • 如果是,请使用&mut self
  • 如果没有,请使用self

考虑std::thread::Builder,它是std::thread::Thread 的构建器。它在内部使用Option 字段来配置如何构建线程:

pub struct Builder {
    name: Option<String>,
    stack_size: Option<usize>,
}

它使用self.spawn() 线程,因为它需要name 的所有权。它理论上可以使用&amp;mut self.take() 字段外的名称,但随后对.spawn() 的调用不会产生相同的结果,这是一个糟糕的设计。它可以选择.clone() 名称,但是生成线程会产生额外且通常不需要的成本。使用 &amp;mut self 会造成不利影响。

考虑std::process::Command,它充当std::process::Child 的构建器。它具有包含程序、参数、环境和管道配置的字段:

pub struct Command {
    program: CString,
    args: Vec<CString>,
    env: CommandEnv,
    stdin: Option<Stdio>,
    stdout: Option<Stdio>,
    stderr: Option<Stdio>,
    // ...
}

它使用&amp;mut self.spawn(),因为它拥有这些字段的所有权来创建Child。无论如何,它必须在内部将所有数据复制到操作系统,因此没有理由使用self。生成具有相同配置的多个子进程还有一个切实的好处和用例。

考虑std::fs::OpenOptions,它充当std::fs::File 的构建器。它只存储基本配置:

pub struct OpenOptions {
    read: bool,
    write: bool,
    append: bool,
    truncate: bool,
    create: bool,
    create_new: bool,
    // ...
}

它使用&amp;mut self.open(),因为它不需要拥有任何东西才能工作。它有点类似于线程构建器,因为有一个与文件关联的路径,就像有一个与线程关联的名称一样,但是,文件路径仅传递给.open(),而不是与构建器一起存储。有一个用例可以打开具有相同配置的多个文件。


上述注意事项实际上只涵盖了.build() 方法中self 的语义,但有充分的理由表明,如果您选择一种方法,您也应该将其用于临时方法:

  • API 一致性
  • (&amp;mut self) -&gt; &amp;mut Self 链接到build(self) 显然不会编译
  • (self) -&gt; Self 用于build(&amp;mut self) 会限制构建器长期重用的灵活性

【讨论】:

    猜你喜欢
    • 2012-09-14
    • 2012-06-25
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    • 2015-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多