Is this idiomatic rust?

I am using the std::path module to get file name from path. I have come up with the code below. Is this idiomatic rust? Seems like I am doing quite a bit of unwrapping and conversion just to get a `String' in the end. Is there a better - idiomatic - way perhaps?

 match env::args().nth(0){
       Some(p) => Path::new(&p)
           .file_name()
           .unwrap()
           .to_str()
           .unwrap()
           .to_string(),
        None => "cp".to_string(),
    }
1 Like

I wonder why Rust doesn't expose methods for getting the str directly, but there sure is a reason for that.

Well, whereever there can go something wrong, there needs to be an Option or Result.

In this case, the .file_name() of course must not simply return the str, as at could be that there is None to return.

Also the first conversion yields a one of these types, because the OsStr might not be convertible to valid UTF-8.

If these unwrap()s were not needed, something could go wrong, which, by design, should not be possible in Rust.

The explicit unwrapping of objects is something that could be automated, but isn't for your convenience.
Just think of a random NullPointerException in Java or a Segmentation fault in C. These things are hard to find.
Rust makes it easy for you to not even have those problems, because if you write unwrap() you know, there should have been some sort of error handling.

If your codes resides in a function that does return a result, I would suggest to make that a Result-type value and use the try! macro.

If everything goes well, return Ok(whatever_you_want_to_return).

1 Like

Well, you could always do something like this:

let s = env::args().nth(0).as_ref().map(|p| {
    Path::new(p).file_name()
        .and_then(OsStr::to_str)
        .expect("error: the first argument is not a file \
                 system path representable in UTF-8.")
}).unwrap_or("cp").to_owned();

Or you could do something like this to pass the error up the stack.

1 Like

nice! :slight_smile: Thanks.

.unwrap()
.to_str()
.unwrap()
.to_string(),

Could someone explain why there needs to be 2x .unwrap() and what's the difference between "to_str()" and "to_string" please?

1 Like
// Borrow the OsString `p` and create a Path from it (the Path is really
// just a "view" into `p`)
Path::new(&p)
    // returns Option<&OsStr> because not all paths end with a file name.
    .file_name()
    // Unwrap to &OsStr (assert that there is a filename)
    .unwrap()
    // Convert to utf8. Not all OsStrs are utf8 so this returns Option<&str>
    .to_str()
    // unwrap to &str (assert that the filename is valid utf8)
    .unwrap()
    // Convert to an owned String. The Path, OsStr, and str all borrow from
    // original argument `p` (they don't allocate) so none of them can outlive
    // `p`. However, we don't want to have to keep `p` around so we need to
    // allocate a new String and store the result there.
    .to_string()

2 Likes