Help with returning value from func with reference

Firstly, I'd like to compliment the rust community for welcoming people like me who have programmed in languages like Python, JavaScript etc and not C, C++ etc. It's an attitude like this that will help push Rust forward.

Also I'd like to thank whomever write the compiler, as its messages are super helpful.

That said, I have the need to extract the file name of a path that is not known in advance. My understanding is that a string not known in advance (e.g. gotten from program args, a config file etc.) must be a String type, as that's stored on the heap, not the stack and can't be determined at compile-time. Is that right?

The problem is, in the below function I return a struct that indirectly contains a reference to a string and thus get a compile-time error : cannot return value referencing local data f.path`.

Here's an excerpt of the code in question:

...
#[derive(Debug)]
struct SyncData<'a> {
    access_token: &'a String,
    file_name: Box<&'a OsStr>,
    file_modified: DateTime<Utc>
}
fn get_sync_data(access_token: &String, f: config::GistFile) -> Result<SyncData, Box<dyn Error>> {
    // Get file name from GistFile struct's path prop, which is a String.
   // for now ignore error handling and unwrap.
    let fname = Path::new(&f.path).file_name().unwrap();
    Ok(SyncData {
        file_name: Box::new(fname),
        access_token: access_token,
        file_modified: fs::metadata(fname)?.modified()?.into(),
    })
}

My understanding is that this error occurs because f.path is dropped at the end of the get_sync_data function, but the SyncData "instance" returned from the function contains a reference to the dropped f.path variable. Rust complains because of what in C could be a dangling pointer. Is that right, and if so how do I go about addressing this?

file_name should probably be an OsString. It's the owned counterpart to OsStr in the same way String is the owned version of str.

Yes. You also do not need the Box around the OsStr. Just use an OsString.

I'm not sure how that solves my problem. It seems Path::new expects a reference to a string. If I change the struct so that file_name is an OsString and try to get a Path by converting f.path which is a String to an OsString I can't construct the Path because it expects a ref. If I use a ref, I get the original error because of a dangling pointer....

I meant something like this:

#[derive(Debug)]
struct SyncData<'a> {
    access_token: &'a str,
    file_name: OsString,
    file_modified: DateTime<Utc>
}

fn get_sync_data(access_token: &str, f: GistFile) -> Result<SyncData, Box<dyn Error>> {
    // Get file name from GistFile struct's path prop, which is a String.
   // for now ignore error handling and unwrap.
    let fname = OsString::from(Path::new(&f.path).file_name().unwrap());
    Ok(SyncData {
        access_token: access_token,
        file_modified: fs::metadata(&fname)?.modified()?.into(),
        file_name: fname,
    })
}

Playground

(I also replaced &String with &str because it's strictly more general. You can still pass an &String there due to auto-dereferencing.)

This is great and works. Thanks for helping.

Instead of unwrapping, now I tried an early return using ok_or and taking advantage of the boxed error kind of like this:

...
Path::new(&f.path).file_name().ok_or("invalid file name")?;
...

If I understand correctly this returns early if there's a problem getting the file name and converts None to Error, correct?

How could I check behavior of this? How would I create a file name on OSx that would trigger this error?

If you read the documentation, you will see that it:

Returns None if the path terminates in ..