Is there a way to avoid cloning when converting a PathBuf to a String?
Absolutely. However, that's not what you are doing. You are taking a part of the PathBuf via file_name and converting that. You cannot take ownership of a part of a string.
If you weren't taking a subset, then converting an entire PathBuf can be done by converting to an OsString and then to a String. Here, I ignore the specific errors and just return success or failure:
use std::path::PathBuf;
fn exe_name() -> Option<String> {
std::env::current_exe()
.ok()
.map(PathBuf::into_os_string)
.and_then(|exe| exe.into_string().ok())
}
Is there any way to avoid this &OsStr -> &str -> String -> &str cycle?
No, because you are creating the String (or OsString or PathBuf, whichever holds ownership depending on the variant of code) inside your method. Check out Return local String as a slice (&str) for why you cannot return a reference to a stack-allocated item (including a string).
As stated in that Q&A, if you want to have references, the thing owning the data has to outlive the references:
use std::env;
use std::path::Path;
use std::ffi::OsStr;
fn binary_name(path: &Path) -> Option<&str> {
path.file_name().and_then(OsStr::to_str)
}
fn main() {
let exe = env::current_exe().ok();
match exe.as_ref().and_then(|e| binary_name(e)) {
Some("cat") => println!("Called as cat"),
Some("dog") => println!("Called as dog"),
Some(other) => println!("Why did you call me {}?", other),
None => println!("Not able to figure out what I was called as"),
}
}
Your original code can be written to not crash on errors easily enough
fn binary_name() -> Option<String> {
let exe = std::env::current_exe();
exe.ok()
.as_ref()
.and_then(|p| p.file_name())
.and_then(|s| s.to_str())
.map(String::from)
}