2

我需要简单地(并且危险地 - 为简洁起见省略错误处理)获取当前的可执行文件名称。我让它工作了,但是我的函数将 a 转换&strString仅在以后调用as_str()它以进行模式匹配。

fn binary_name() -> String {
    std::env::current_exe().unwrap().file_name().unwrap().to_str().unwrap().to_string()
}

据我了解,std::env::current_exe()给了PathBuf我可以通过归还它来转让的所有权。就目前而言,我借它来将其转换为&str. 从那里,返回字符串的唯一方法是在PathBuf删除之前克隆它。

有什么办法可以避免这个&OsStr -> &str -> String -> &str循环吗?

4

1 回答 1

4

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)
}
于 2017-01-23T15:40:04.370 回答