如何附加到 PathBuf



有没有一种优雅的方法可以将后缀(如.bak)附加到Path并获得新PathBuf?像这样:

let p = Path::new("foo.c");
let p_bak = /* ? */;
assert_eq!(p_bak, Path::new("foo.c.bak"));

使用字符串,可以使用format!("{}.bak", file_name).有了路径,我看不到明显的等价物。with_extension()并没有完全做到这一点,因为p.with_extension("bak")会创建foo.bak而不是所需的foo.c.bak

最"明显"的解决方案是定义一个append_to_path()并使用append_to_path(p, ".bak")

fn append_to_path(p: &Path, s: &str) -> PathBuf {
let mut p_osstr = p.as_os_str().to_owned();
p_osstr.push(s);
p_osstr.into()
}

有没有更短的表达方式?

tap箱允许将其放在一个流线型的表达中,但它仍然感觉相当神秘:

let p_bak: PathBuf = p.as_os_str().to_owned().tap_mut(|s| s.push(".bak")).into();

我认为没有任何更短的方法可以做到这一点。但是,我们可以以稍微高效的方式编写函数。

fn append_to_path(p: PathBuf, s: &str) -> PathBuf {
let mut p = p.into_os_string();
p.push(s);
p.into()
}

into_os_string()as_os_str().to_owned()更有效率。into_os_string()as_os_str()都非常便宜,但to_owned()克隆整个路径,这需要动态内存分配。

但是,into_os_string()只能在PathBuf上使用,不能用于&Path,因此我们需要相应地更改签名。通过按值获取PathBuf,我们将克隆PathBuf的责任转移给调用方,但它也使调用方能够在不需要时避免克隆。

我们还可以通过使用Into特性使函数更方便使用:

fn append_to_path(p: impl Into<OsString>, s: impl AsRef<OsStr>) -> PathBuf {
let mut p = p.into();
p.push(s);
p.into()
}

&PathPathBuf实现Into<OsString>,因此我们可以将这些类型的值直接传递给函数的第一个参数。当&Path转换为OsString时,&PathInto实现将执行动态内存分配,但是当PathBuf被转换时,不会发生内存分配。对于第二个参数,我只是使用了与OsString::push对其s参数相同的特征边界。

let p_bak: PathBuf = (p.to_string_lossy().to_string() + ".bak").into();
// or
let p_bak: PathBuf = PathBuf::from(p.to_string_lossy().to_string() + ".bak");

使用to_string_lossy避免使用to_string返回Option<&str> 但应仅在我们知道字符串将由有效的 unicode 字符组成时才使用

相关内容

  • 没有找到相关文章

最新更新