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

时间:2015-02-05 21:47:19

标签: rust lifetime borrow-checker

  

编者注:在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是静态的。

我试过这种方式,但是我得到了相同的结果,编译器抱怨“路径”的生存时间不够长。

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

2 个答案:

答案 0 :(得分: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阻止你引用不再分配的内存,不管是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
  6. 然而,我并不是所有解缠的粉丝。我写这个函数是这样的:

    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

答案 1 :(得分:0)

一个小相关点:

  

我认为&amp; str是静态的。

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