如何编写存储路径的构建器?
How can I write a builder that stores a Path?
Path
参数可以立即转换为 PathBuf
,但这似乎效率低下。必须有一些方法只保留一个 Path
,对吗?
use std::{fs::File, path::Path};
struct Foo {
a: Option<File>,
b: Option<File>,
}
struct FooBuilder<'a> {
a: Option<&'a Path>,
b: Option<&'a Path>,
}
impl<'a> FooBuilder<'a> {
fn new() -> FooBuilder<'a> {
FooBuilder { a: None, b: None }
}
fn a<P: AsRef<Path> + 'a>(&'a mut self, a: P) -> &mut FooBuilder<'a> {
self.a = Some(a.as_ref());
self
}
fn b<P: AsRef<Path> + 'a>(&'a mut self, b: P) -> &mut FooBuilder<'a> {
self.b = Some(b.as_ref());
self
}
fn done(&self) -> Foo {
Foo {
a: match self.a {
Some(path) => Some(File::open(path).unwrap()),
None => None,
},
b: match self.b {
Some(path) => Some(File::open(path).unwrap()),
None => None,
},
}
}
}
fn main() {
let path1 = Path::new("1");
let path2 = Path::new("2");
let _foo = FooBuilder::new().a(path1).b(path2).done();
}
error[E0597]: `a` does not live long enough
--> src/main.rs:19:23
|
13 | impl<'a> FooBuilder<'a> {
| -- lifetime `'a` defined here
...
19 | self.a = Some(a.as_ref());
| --------------^----------
| | |
| | borrowed value does not live long enough
| assignment requires that `a` is borrowed for `'a`
20 | self
21 | }
| - `a` dropped here while still borrowed
error[E0597]: `b` does not live long enough
--> src/main.rs:24:23
|
13 | impl<'a> FooBuilder<'a> {
| -- lifetime `'a` defined here
...
24 | self.b = Some(b.as_ref());
| --------------^----------
| | |
| | borrowed value does not live long enough
| assignment requires that `b` is borrowed for `'a`
25 | self
26 | }
| - `b` dropped here while still borrowed
这个有效:
use std::{fs::File, path::Path};
struct Foo {
a: Option<File>,
}
struct FooBuilder<'a> {
a: Option<&'a Path>,
}
impl<'a> FooBuilder<'a> {
fn new() -> FooBuilder<'a> {
FooBuilder { a: None }
}
fn a<P>(&mut self, a: &'a P) -> &mut FooBuilder<'a>
where
P: AsRef<Path> + ?Sized,
{
self.a = Some(a.as_ref());
self
}
fn build(&self) -> Foo {
Foo {
a: self.a.map(|path| File::open(path).unwrap()),
}
}
}
fn main() {
let path1 = Path::new("1");
let _foo = FooBuilder::new().a(path1).build();
}
让我们关注a
方法:
fn a<P>(&mut self, a: &'a P) -> &mut FooBuilder<'a>
where
P: AsRef<Path> + ?Sized,
此方法接受对实现 AsRef<Path>
的类型的 引用 。这意味着我们可以获得与参数具有相同生命周期的 Path
的引用。另一个变化是通过 ?
使 Sized
绑定对于类型是可选的。这意味着我们可以参考一些我们不知道它有多大的东西。这很好,因为我们会知道 引用本身 有多大。
让我们将其与您的原始版本进行比较:
fn a<P: AsRef<Path> + 'a>(&'a mut self, a: P) -> &mut FooBuilder<'a> {
self.a = Some(a.as_ref());
self
}
此处,a
参数按值传递到方法 a
中。当您调用 as_ref
时,您是在 reference 上隐式调用它,指向方法调用的堆栈帧上的项目。引用的项目将在方法调用结束时被删除,这意味着引用将变得无效。这就是您遇到 error: `a` does not live long enough
错误的原因。
我也用Option::map
清理了build
方法。我将它重命名为 build
因为 builders 通常应该有一个 build
方法,除非有更明显的动词要使用(比如 open
)。
另请参阅:
Path
参数可以立即转换为 PathBuf
,但这似乎效率低下。必须有一些方法只保留一个 Path
,对吗?
use std::{fs::File, path::Path};
struct Foo {
a: Option<File>,
b: Option<File>,
}
struct FooBuilder<'a> {
a: Option<&'a Path>,
b: Option<&'a Path>,
}
impl<'a> FooBuilder<'a> {
fn new() -> FooBuilder<'a> {
FooBuilder { a: None, b: None }
}
fn a<P: AsRef<Path> + 'a>(&'a mut self, a: P) -> &mut FooBuilder<'a> {
self.a = Some(a.as_ref());
self
}
fn b<P: AsRef<Path> + 'a>(&'a mut self, b: P) -> &mut FooBuilder<'a> {
self.b = Some(b.as_ref());
self
}
fn done(&self) -> Foo {
Foo {
a: match self.a {
Some(path) => Some(File::open(path).unwrap()),
None => None,
},
b: match self.b {
Some(path) => Some(File::open(path).unwrap()),
None => None,
},
}
}
}
fn main() {
let path1 = Path::new("1");
let path2 = Path::new("2");
let _foo = FooBuilder::new().a(path1).b(path2).done();
}
error[E0597]: `a` does not live long enough
--> src/main.rs:19:23
|
13 | impl<'a> FooBuilder<'a> {
| -- lifetime `'a` defined here
...
19 | self.a = Some(a.as_ref());
| --------------^----------
| | |
| | borrowed value does not live long enough
| assignment requires that `a` is borrowed for `'a`
20 | self
21 | }
| - `a` dropped here while still borrowed
error[E0597]: `b` does not live long enough
--> src/main.rs:24:23
|
13 | impl<'a> FooBuilder<'a> {
| -- lifetime `'a` defined here
...
24 | self.b = Some(b.as_ref());
| --------------^----------
| | |
| | borrowed value does not live long enough
| assignment requires that `b` is borrowed for `'a`
25 | self
26 | }
| - `b` dropped here while still borrowed
这个有效:
use std::{fs::File, path::Path};
struct Foo {
a: Option<File>,
}
struct FooBuilder<'a> {
a: Option<&'a Path>,
}
impl<'a> FooBuilder<'a> {
fn new() -> FooBuilder<'a> {
FooBuilder { a: None }
}
fn a<P>(&mut self, a: &'a P) -> &mut FooBuilder<'a>
where
P: AsRef<Path> + ?Sized,
{
self.a = Some(a.as_ref());
self
}
fn build(&self) -> Foo {
Foo {
a: self.a.map(|path| File::open(path).unwrap()),
}
}
}
fn main() {
let path1 = Path::new("1");
let _foo = FooBuilder::new().a(path1).build();
}
让我们关注a
方法:
fn a<P>(&mut self, a: &'a P) -> &mut FooBuilder<'a>
where
P: AsRef<Path> + ?Sized,
此方法接受对实现 AsRef<Path>
的类型的 引用 。这意味着我们可以获得与参数具有相同生命周期的 Path
的引用。另一个变化是通过 ?
使 Sized
绑定对于类型是可选的。这意味着我们可以参考一些我们不知道它有多大的东西。这很好,因为我们会知道 引用本身 有多大。
让我们将其与您的原始版本进行比较:
fn a<P: AsRef<Path> + 'a>(&'a mut self, a: P) -> &mut FooBuilder<'a> {
self.a = Some(a.as_ref());
self
}
此处,a
参数按值传递到方法 a
中。当您调用 as_ref
时,您是在 reference 上隐式调用它,指向方法调用的堆栈帧上的项目。引用的项目将在方法调用结束时被删除,这意味着引用将变得无效。这就是您遇到 error: `a` does not live long enough
错误的原因。
我也用Option::map
清理了build
方法。我将它重命名为 build
因为 builders 通常应该有一个 build
方法,除非有更明显的动词要使用(比如 open
)。
另请参阅: