1

Path参数可以立即转换为 a PathBuf,但这似乎效率低下。必须有某种方法来保留 a 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
4

1 回答 1

2

这有效:

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与参数具有相同生命周期的 a 的引用。另一个更改是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上隐式调用它。被引用的项目将在方法调用结束时被删除,这意味着引用将变得无效。这就是您遇到错误的原因。error: `a` does not live long enough

我也用Option::map清理的build方法。我将其重命名为,build因为构建者通常应该有一个build方法,除非有更明显的动词要使用(如open)。

也可以看看:

于 2015-11-06T15:36:32.347 回答