Spr*_*ite 13 design-patterns ownership rust
到目前为止,我在官方 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 { ... }
}
Run Code Online (Sandbox Code Playgroud)
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 { ... }
}
Run Code Online (Sandbox Code Playgroud)
我正在写一个新的箱子,我有点困惑我应该选择哪种模式。我知道如果以后更改一些API会很痛苦,所以我想现在就做出决定。
我理解它们之间的语义差异,但在实际情况下我们应该选择哪一个呢?或者说我们应该如何选择它们呢?为什么?
kmd*_*eko 13
从同一个构建器构建多个值是否有益?
&mut selfself考虑std::thread::Builder哪个是std::thread::Thread. 它Option在内部使用字段来配置如何构建线程:
pub struct Builder {
name: Option<String>,
stack_size: Option<usize>,
}
Run Code Online (Sandbox Code Playgroud)
它使用线程self是.spawn()因为它需要name. 理论上它可以使用字段外的名称,但随后的调用&mut self不会创建相同的结果,这是一个糟糕的设计。它可以选择名称,但是生成线程会产生额外的且通常是不必要的成本。使用就会有损害。.take().spawn().clone()&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>,
// ...
}
Run Code Online (Sandbox Code Playgroud)
它使用&mut selfto 是.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,
// ...
}
Run Code Online (Sandbox Code Playgroud)
它过去是&mut self这样.open(),因为它不需要任何东西的所有权就可以工作。它有点类似于线程构建器,因为有一个与文件关联的路径,就像有一个与线程关联的名称一样,但是,文件路径仅传递到构建器,.open()而不是与构建器一起存储。有一个使用相同配置打开多个文件的用例。
self上面的注意事项实际上只涵盖了方法中的语义.build(),但是有足够的理由表明,如果您选择一种方法,您也应该将其用于临时方法:
(&mut self) -> &mut Self到build(self)显然不会编译(self) -> Selfintobuild(&mut self)会限制构建器长期重用的灵活性另请参阅:如何在 Rust 中使用链式方法调用编写惯用的构建模式?