如何使用 `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>
的参数,以便也允许字符串,例如。
我的第一次尝试,extend1
和 extend2
,没有通过借用检查器,这两种情况都抱怨 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)
}
由于函数的参数仅通过引用使用,因此您可能希望仅通过引用来获取它们。这就像在每个参数中添加一个符号一样简单。
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
功能。
我很难让 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
成功了,但是有一个明显的代码重复的缺点,随着函数体的增长,这个缺点会变得更加严重。
在这种情况下最好的解决方案是什么?
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)
}
由于函数的参数仅通过引用使用,因此您可能希望仅通过引用来获取它们。这就像在每个参数中添加一个符号一样简单。
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
功能。