如何使用 `AsRef` 参数?

How to use `AsRef` arguments?

我很难让 AsRef 以干净的方式工作。

const DEFAULT: &str = "lib";

use std::path::{Path, PathBuf};

fn extend(p: &Path, q: Option<&Path>) -> PathBuf {
    let q: &Path = q.unwrap_or(DEFAULT.as_ref());
    p.join(q)
}

// DOES NOT COMPILE
fn extend1<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Option<Q>) -> PathBuf {
    let q: &Path = q.map(|x| x.as_ref()).unwrap_or(DEFAULT.as_ref());
    p.as_ref().join(q)
}

// DOES NOT COMPILE
fn extend2<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Option<Q>) -> PathBuf {
    let q: &Path = match q {
        Some(x) => x.as_ref(),
        None => DEFAULT.as_ref(),
    };
    p.as_ref().join(q)
}

fn extend3<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Option<Q>) -> PathBuf {
    match q {
        Some(x) => p.as_ref().join(x.as_ref()),
        None => p.as_ref().join(AsRef::<Path>::as_ref(DEFAULT)),
    }
}

函数 extend 使用 Path 引用,但我想将其概括为接受类型 AsRef<Path> 的参数,以便也允许字符串,例如。

我的第一次尝试,extend1extend2,没有通过借用检查器,这两种情况都抱怨 x 的生命周期。

我的第三次尝试 extend3 成功了,但是有一个明显的代码重复的缺点,随着函数体的增长,这个缺点会变得更加严重。

在这种情况下最好的解决方案是什么?

Option::map 消耗内部值(如果有的话)。所以在代码q.map(|x| x.as_ref())中,闭包按值取x。您可以通过注意 q.map(|x: &Q| x.as_ref()) 给出类型错误来检查这一点,而 q.map(|x: Q| x.as_ref()) 不会(它仍然给出生命周期错误)。这意味着当您调用 x.as_ref() 时,将创建一个对 x 的新引用,与任何外部引用无关。这意味着引用仅在闭包内部有效,但您想在 extend1.

的其余部分使用它

您想要做的是借用 q,有效期至 extend1 结束。使用 Option::as_ref()&Option<Q> 转换为 Option<&Q>(只需使用 q.as_ref() 即可将 q 的借用转化为对其内容(如果有)的借用您需要 q 的借用)。然后当你使用 map 时,闭包将占用 &Q,而不是 Q。引用的生命周期将与 q 的外部借用相同,因此它将持续到 extend1 结束(如果需要)。

const DEFAULT: &str = "lib";

use std::path::{Path, PathBuf};

fn extend1<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Option<Q>) -> PathBuf {
    let q: &Path = q.as_ref().map(|x| x.as_ref()).unwrap_or(DEFAULT.as_ref());
    p.as_ref().join(q)
}

(playground)

由于函数的参数仅通过引用使用,因此您可能希望仅通过引用来获取它们。这就像在每个参数中添加一个符号一样简单。

const DEFAULT: &str = "lib";

use std::path::{Path, PathBuf};

fn extend1<P: AsRef<Path>, Q: AsRef<Path>>(p: &P, q: &Option<Q>) -> PathBuf {
    let q: &Path = q.as_ref().map(|x| x.as_ref()).unwrap_or(DEFAULT.as_ref());
    p.as_ref().join(q)
}

(playground)

const DEFAULT: &str = "lib";

use std::path::{Path, PathBuf};

fn extend1<P: AsRef<Path>, Q: AsRef<Path>>(p: &P, q: Option<&Q>) -> PathBuf {
    let q: &Path = q.map(|x| x.as_ref()).unwrap_or(DEFAULT.as_ref());
    p.as_ref().join(q)
}

(playground)

请注意,最后一个版本不需要调用 q.as_ref(),因为 q 已经具有类型 Option<&Q>。此版本最接近您原来的 extend 功能。