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.
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:
snake_case
for itemsreturn
statements unless you are exiting from a function early.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:
Result
to propagate errors to the caller.DirEntry
for the filename.OsString
.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With