我很难让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>
的参数,以便允许例如字符串。
我的第一个尝试extend1
和extend2
没有通过借阅检查器,后者在两种情况下都抱怨x
的生存期。
我的第三次尝试extend3
可以工作,但是有代码重复的明显缺点,随着功能体的增长,代码重复变得越来越严重。
在这种情况下最好的解决方案是什么?
答案 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
结束为止。可以使用q
将Option::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)
}
由于函数的参数仅通过引用使用,因此您可能希望仅通过引用使用它们。这就像在每个参数中添加与号一样简单。
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)
}
或
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)
}
请注意,此最新版本不需要调用q.as_ref()
,因为q
已具有类型Option<&Q>
。此版本最接近您的原始extend
函数。