Borrowed value does not live long enough when creating a Vec

*爱你&永不变心* 提交于 2019-12-04 11:28:22

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.

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.

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