创建 Vec 时,借用的值不够长

Borrowed value does not live long enough when creating a Vec

Editor's note: This question was asked before Rust 1.0. Since then, many functions and types have changed, as have certain language semantics. The code in the question is no longer valid, but the ideas expressed in the answers may be.

我正在尝试列出目录中的文件并将文件名复制到我自己的 Vec。我尝试了几种解决方案,但总是以无法创建足够长的生存变量的问题告终。我不明白我的错误。

fn getList(action_dir_path : &str) -> Vec<&str> {
    let v = fs::readdir(&Path::new(action_dir_path))
            .unwrap()
            .iter()
            .map(|&x| x.filestem_str().unwrap())
            .collect();
    return v;
}

为什么编译器会抱怨 "x"?我不关心 x,我想要它里面的 &str,我认为 &str 是静态的。

我试过这种方法,但我得到了相同的结果,编译器抱怨 "paths" 活得不够长。

fn getList2(action_dir_path : &str) -> Vec<&str> {
    let paths = fs::readdir(&Path::new(action_dir_path)).unwrap();
    let mut v : Vec<&str> = Vec::new();

    for path in paths.iter(){
       let aSlice = path.filestem_str().unwrap();
       v.push(aSlice);
    }

    return v;
}

这里是the playground.

支持 Rust 1.0 的代码的最直译是:

use std::{fs, path::Path, ffi::OsStr};

fn getList(action_dir_path: &str) -> Vec<&OsStr> {
    let v = fs::read_dir(&Path::new(action_dir_path))
        .unwrap()
        .map(|x| x.unwrap().path().file_stem().unwrap())
        .collect();
    return v;
}

这会产生错误消息:

生锈 2015

error[E0597]: borrowed value does not live long enough
 --> src/lib.rs:6:18
  |
6 |         .map(|x| x.unwrap().path().file_stem().unwrap())
  |                  ^^^^^^^^^^^^^^^^^                    - temporary value only lives until here
  |                  |
  |                  temporary value does not live long enough
  |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 3:1...
 --> src/lib.rs:3:1
  |
3 | / fn getList(action_dir_path: &str) -> Vec<&OsStr> {
4 | |     let v = fs::read_dir(&Path::new(action_dir_path))
5 | |         .unwrap()
6 | |         .map(|x| x.unwrap().path().file_stem().unwrap())
7 | |         .collect();
8 | |     return v;
9 | | }
  | |_^

生锈 2018

error[E0515]: cannot return value referencing temporary value
 --> src/lib.rs:6:18
  |
6 |         .map(|x| x.unwrap().path().file_stem().unwrap())
  |                  -----------------^^^^^^^^^^^^^^^^^^^^^
  |                  |
  |                  returns a value referencing data owned by the current function
  |                  temporary value created here

问题来自Path::file_stem。这是签名:

pub fn file_stem(&self) -> Option<&OsStr>

这表示该方法将 return 借用对 OsStr 的引用。 PathBuf 结构是字符串的 owner。当您离开该方法时,没有任何地方拥有 PathBuf,因此它将被删除。这意味着对 PathBuf 的任何引用都将不再有效。这是 Rust 阻止你引用不再分配的内存,对 Rust 来说是的!

您可以做的最简单的事情是 return 一个 Vec<String>String 拥有其中的字符串,所以我们不用担心它在我们离开函数时被释放:

fn get_list(action_dir_path: &str) -> Vec<String> {
    fs::read_dir(action_dir_path)
        .unwrap()
        .map(|x| {
            x.unwrap()
                .path()
                .file_stem()
                .unwrap()
                .to_str()
                .unwrap()
                .to_string()
        })
        .collect()
}

我还更新了样式(免费!),使其更像 Rust:

  1. 对项目使用 snake_case
  2. 类型定义中冒号前没有space
  3. 没有理由只为 return 设置一个变量。
  4. 不要使用明确的 return 语句,除非您提前退出函数。
  5. 无需将路径包裹在 Path.

但是,我不喜欢所有的展开。我会这样写函数:

use std::{ffi::OsString, fs, io, path::Path};

fn get_list(action_dir_path: impl AsRef<Path>) -> io::Result<Vec<OsString>> {
    fs::read_dir(action_dir_path)?
        .map(|entry| entry.map(|e| e.file_name()))
        .collect()
}

fn main() {
    println!("{:?}", get_list("/etc"));
}

除上述变化外:

  1. 我使用通用类型作为输入路径。
  2. I return a Result 将错误传播给调用者。
  3. 我直接问 DirEntry 文件名。
  4. 我将类型保留为 OsString

一个小相关点:

I thought &str were static.

&'static str是静态的,但那只是&str的一种。它可以有任何类型的生命周期。