如何使用`AsRef`参数?

问题描述 投票:2回答:1

[我很难使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,虽然有效,但是具有代码重复的明显缺点,随着功能体的增长,它会变得更加严重。

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

reference rust lifetime borrow-checker borrowing
1个回答
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函数。

© www.soinside.com 2019 - 2024. All rights reserved.