Borrowed value does not live long enough when creating a Vec

那年仲夏 提交于 2020-01-01 12:15:24

问题


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.

I'm trying to list the files in a directory and copy the filename to my own Vec. I've tried several solutions, but it always ends up with a problem of not being able to create long enough living variables. I don't understand my mistake.

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;
}

Why does the compiler complain about "x" ? I don't care about x, I want the &str inside it and I thought &str were static.

I tried this way, but I got the same result with the compiler complaining about "paths" not living long enough.

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;
}

Here is the playground.


回答1:


The most literal translation of your code that supports Rust 1.0 is this:

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;
}

This produces the error messages:

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

The problem comes from Path::file_stem. This is the signature:

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

This indicates that the method will return a borrowed reference to a OsStr. The PathBuf struct is the owner of the string. When you leave the method, there's nowhere left that owns the PathBuf, so it will be dropped. This means that any references into the PathBuf will no longer be valid. This is Rust preventing you from having references to memory that is no longer allocated, yay for Rust!

The easiest thing you can do is return a Vec<String>. String owns the string inside of it, so we don't need to worry about it being freed when we leave the function:

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()
}

I also updated the style (at no charge!) to be more Rust-like:

  1. Use snake_case for items
  2. No space before the colon in type definitions
  3. There's no reason to set a variable just to return it.
  4. Don't use explicit return statements unless you are exiting from a function early.
  5. There's no need to wrap the path in a Path.

However, I'm not a fan of all of the unwrapping. I'd write the function like this:

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"));
}

In addition to the changes above:

  1. I use a generic type for the input path.
  2. I return a Result to propagate errors to the caller.
  3. I directly ask the DirEntry for the filename.
  4. I leave the type as an OsString.



回答2:


One small related point:

I thought &str were static.

&'static strs are static, but that's only one kind of &str. It can have any kind of lifetime.



来源:https://stackoverflow.com/questions/28354693/borrowed-value-does-not-live-long-enough-when-creating-a-vec

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!