Pushing a struct to a vector in a loop

Hey there! I'm brand new to Rust and trying to read a directory of Markdown files. From there, I want to get the first line (among other things.)

I think I want a struct for this, since I'll want to perform several operations on the files. I've managed to get this code pieced together. I think it sounds reasonable, but it doesn't compile due to some lifetime errors.

pub struct Zettel<'a> {
    path: &'a Path,
}

pub fn in_dir(directory: &Path) -> Result<Vec<Zettel>> {
    let mut out = Vec::new();

    for entry in fs::read_dir(directory)? {
        out.push(Zettel {
            path: &entry?.path(),
        })
    }

    Ok(out)
}

Specifically, the compiler gives me this error:

error[E0515]: cannot return value referencing temporary value
  --> src/lib.rs:22:5
   |
18 |             path: &entry?.path(),
   |                    ------------- temporary value created here
...
22 |     Ok(out)
   |     ^^^^^^^ returns a value referencing data owned by the current function

That seems reasonable to me (it goes out of scope after each iteration of the loop, right? And gets destroyed then?), but I'm not sure what I can do that will make this work!

I've already tried a bunch of things on &entry?.path(): to_owned(), clone(), etc. I've also tried rewriting it in the to_iter/map/collect style and get the same error.

What's going on here? Surely this is just a gap in my knowledge!

.clone() will only work if you store a PathBuf instead of a Path. Path is inherently a reference type. It needs to have an owned object somewhere managing its lifetime.

DirEntry hands out owned PathBufs, so if you want to hold on to them then you need to store them somewhere or they'll get dropped at the end of your function and any references will no longer be valid. So perhaps using PathBuf instead of &'a Path in your struct might do the trick.

Yeah, storing PathBuf seems to work, but I don't understand why yet—would you mind helping with that?

From reading the docs, it looks like PathBuf is to Path as String is to str. So that means… I think… that it's got a known size which points to an unknown size somewhere else? Is that it?

It's pretty much it. An owned something (like a PathBuf) might have some data allocated on the heap somewhere which gets cleaned up once the owner is done with it. But a Path is a reference to data owned by someone else and doesn't need to do any sort of cleanup once finished. However, since it is just a reference, Rust won't let it live longer than the owned value since that would mean pointing at something no longer valid.

1 Like

hmm, I'll think on this as I'm internalizing how the borrow checker works. Thank you for the explanation!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.