如何使用`AsRef`参数?

时间:2019-09-27 17:18:46

标签: reference rust lifetime borrow-checker borrowing

我很难让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)),
    }
}

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

我的第一个尝试extend1extend2没有通过借阅检查器,后者在两种情况下都抱怨x的生存期。

我的第三次尝试extend3可以工作,但是有代码重复的明显缺点,随着功能体的增长,代码重复变得越来越严重。

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

1 个答案:

答案 0 :(得分:1)

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结束为止。可以使用qOption::as_ref()转换为&Option<Q>(简单地使用Option<&Q>q.as_ref()的借用变成其内容的借用(如果有)。您需要借用的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函数。