使用构建器模式时,我应该按值还是可变引用获取“self”?

2024-03-01

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

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 { ... }
}

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

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


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

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

考虑std::thread::Builder https://doc.rust-lang.org/std/thread/struct.Builder.html这是一个建设者std::thread::Thread https://doc.rust-lang.org/std/thread/struct.Thread.html。它用Option内部字段用于配置如何构建线程:

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

It uses self to .spawn()线程,因为它需要所有权name。理论上可以使用&mut self and .take()名称超出字段,但随后调用.spawn()不会产生相同的结果,这是一个糟糕的设计。它可以选择.clone()名称,但是生成线程会产生额外的且通常不需要的成本。使用&mut self将是一种损害。

考虑std::process::Command https://doc.rust-lang.org/std/process/struct.Command.html它作为一个建设者std::process::Child https://doc.rust-lang.org/std/process/struct.Child.html。它的字段包含程序、参数、环境和管道配置:

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

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

考虑std::fs::OpenOptions https://doc.rust-lang.org/std/fs/struct.OpenOptions.html它作为一个建设者std::fs::File https://doc.rust-lang.org/std/fs/struct.File.html。它只存储基本配置:

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

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


上面的考虑实际上只涵盖了语义self in the .build()方法,但有足够的理由表明,如果您选择一种方法,您也应该将其用于临时方法:

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

也可以看看:如何在 Rust 中使用链式方法调用编写惯用的构建模式? https://stackoverflow.com/questions/41617182

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用构建器模式时,我应该按值还是可变引用获取“self”? 的相关文章

随机推荐