path - 如何编写存储路径的构建器?

标签 path rust borrowing

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通过 ? 为类型绑定(bind)可选.这意味着我们可以引用一些我们不知道它有多大的东西。这很好,因为我们将知道引用本身 有多大。

让我们将其与您的原始版本进行比较:

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 者通常应该有一个build方法,除非使用更明显的动词(如 open )。

另见:

关于path - 如何编写存储路径的构建器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33569651/

相关文章:

dictionary - HashMap *_equiv 方法 - 无法通过等效键更新

rust - 存储一个引用该对象中的对象的盒装闭包

hashmap - 使用入口模式时如何改变 HashMap 的其他元素?

`for...in` 循环中的 Rust 借用规则

java - 在 Java 中加入远程路径

java - java jar 中的路径问题

arrays - 创建相同长度的数组

c++ - 查找相对路径

debugging - GDB 源路径

rust - 在 Rust 中修剪集合的输入行