创建Vec时,借用的值存活时间不足

4

编辑注:此问题是在Rust 1.0之前提出的。此后,许多函数和类型已更改,某些语言语义也已更改。问题中的代码已不再有效,但答案中表达的思想可能仍然有用。

我正在尝试列出目录中的文件并将文件名复制到自己的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;
}

这里是代码在线编辑器
2个回答

7
您的代码最直接支持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;
}

这会产生以下错误信息:
Rust 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 | | }
  | |_^

Rust 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>

这表示该方法将返回对OsStr借用引用PathBuf结构体是字符串的所有者。当您离开该方法时,没有地方拥有PathBuf,因此它将被删除。这意味着任何引用到PathBuf中的内容都将不再有效。这是Rust防止您引用已释放内存的方式,值得庆幸!
最简单的方法是返回一个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. 类型定义中冒号前不要留空格
  3. 没有必要设置变量再返回它。
  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. 我返回一个Result以将错误传播给调用者。
  3. 我直接从DirEntry获取文件名。
  4. 我保留OsString类型。

谢谢你的回答,我现在明白了!感谢你免费提供的纠正和建议!! - mrburne

0
一个相关的小点:
“我以为&str是静态的。”
&'static str是静态的,但那只是一种&str。它可以有任何类型的生命周期。

是的,你说得对。我误读了Rust书中关于&str的段落,它实际上是在讲字符串字面量。http://doc.rust-lang.org/book/more-strings.html - mrburne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接