【问题标题】:How can I write a builder that stores a Path?如何编写存储路径的构建器?
【发布时间】:2015-11-06 15:01:45
【问题描述】:

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

【问题讨论】:

    标签: path rust borrowing


    【解决方案1】:

    这行得通:

    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&lt;Path&gt; 的类型的引用。这意味着我们可以获得与参数具有相同生命周期的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)。

    另见:

    【讨论】:

    • 这对我有帮助。我想添加一些在 rust 书中多次声明的内容,以及在这里隐含的内容,但仍然可能会让刚接触 rust 的人感到困惑:Sized 是默认的和隐含的。
    猜你喜欢
    • 2017-05-20
    • 1970-01-01
    • 1970-01-01
    • 2016-10-09
    • 1970-01-01
    • 1970-01-01
    • 2019-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多