我试图编写一个函数,使用闭包验证给定的集合 . 该函数获取集合的所有权,遍历内容,如果未找到无效项,则返回集合的所有权 . 这样就可以像这样使用(不为 Vec
创建一个temp): let col = validate(vec![1, 2], |&v| v < 10)?;
这是该函数的当前实现:
use std::fmt::Debug;
fn validate<C, F, V>(col: C, pred: F) -> Result<C, String>
where C: Debug,
for<'c> &'c C: IntoIterator<Item = V>,
F: Fn(&V) -> bool,
V: Debug
{
if let Some(val) = (&col).into_iter().find(|v| !pred(v)) {
Err(format!("{:?} contains invalid item: {:?}.", col, val))?;
}
Ok(col)
}
它确实编译,但是当我尝试使用它时它不起作用:
use std::collections::BTreeMap;
use std::iter::{FromIterator, once};
fn main() {
println!("Vec: {:?}", validate(vec![1, 2, 3, 4], |&&v| v <= 3));
// ^^^^^^^^ expected bound lifetime parameter 'c, found concrete lifetime
println!("Map: {:?}",
validate(BTreeMap::from_iter(once((1, 2))), |&(&k, &v)| k <= 3));
}
我想在这里完成的是可能的吗?
背景
我正在为我的玩具项目编写解析器,并且想知道我是否可以编写一个与我使用的所有集合类型一起使用的 validate
函数:Vec s,VecDeque s,BTreeSet s,BTreeMap s,&[T] slices .
这些集合中的每一个都为其自身的引用实现 IntoIterator
特征,可用于在引用上调用 .into_iter()
而不消耗集合中的项:
这是函数声明中引用的 for<'c> &'c C: IntoIterator<Item = V>
. 由于引用被定义为 in 函数体本身,我们可以在函数上声明't just use a lifetime that'(如 fn validate<'c, ...
),因为这意味着引用必须比函数更长(它不能) . 相反,我们必须使用Higher-Rank Trait Bound来声明这个生命周期 .
在我看来,这个生命周期也是问题的根源,因为获取并返回对集合的引用的函数版本工作正常:
// This works just fine.
fn validate<'c, C, F, V>(col: &'c C, pred: F) -> Result<&'c C, String>
where C: Debug,
&'c C: IntoIterator<Item = V>,
F: Fn(&V) -> bool,
V: Debug
{
if let Some(val) = col.into_iter().find(|v| !pred(v)) {
Err(format!("{:?} contains invalid item: {:?}.", col, val))?;
}
Ok(col)
}
此外,我设法实现了该函数的另外两个版本,一个适用于 Vec
, VecDeque
, BTreeSet
和 &[T] slices
,另一个适用于 BTreeMap
,可能还适用于其他映射:
use std::fmt::Debug;
pub fn validate_collection<C, F, V>(col: C, pred: F) -> Result<C, String>
where C: Debug,
for<'c> &'c C: IntoIterator<Item = &'c V>,
F: Fn(&V) -> bool,
V: Debug
{
if let Some(val) = (&col).into_iter().find(|&v| !pred(v)) {
Err(format!("{:?} contains invalid item: {:?}.", col, val))?;
}
Ok(col)
}
pub fn validate_mapping<C, F, K, V>(col: C, pred: F) -> Result<C, String>
where C: Debug,
for<'c> &'c C: IntoIterator<Item = (&'c K, &'c V)>,
F: Fn(&K, &V) -> bool,
K: Debug,
V: Debug
{
if let Some(val) = (&col).into_iter().find(|&(k, v)| !pred(k, v)) {
Err(format!("{:?} contains invalid item: {:?}.", col, val))?;
}
Ok(col)
}
最后我希望创造一个 Validate
特质 . 目前,我只能将其用于集合或映射,因为impls冲突 .
use std::fmt::Debug;
trait Validate<V>: Sized {
fn validate<F>(self, F) -> Result<Self, String> where F: Fn(&V) -> bool;
}
// Impl that only works for collections, not mappings.
impl<C, V> Validate<V> for C
where C: Debug,
for<'c> &'c C: IntoIterator<Item = &'c V>,
V: Debug
{
fn validate<F>(self, pred: F) -> Result<C, String>
where F: Fn(&V) -> bool
{
if let Some(val) = (&self).into_iter().find(|&v| !pred(v)) {
Err(format!("{:?} contains invalid item: {:?}.", self, val))?;
}
Ok(self)
}
}
fn main() {
println!("Vec: {:?}", vec![1, 2, 3, 4].validate(|&v| v <= 3));
}
1 回答
看着你的特质界限(重新格式化了一点):
问题是
&C
不会实现IntoIterator<Item = V>
;引用倾向于迭代引用 .修复(以及闭包中的额外引用)使其工作:
Playground
要扩展它以使用
BTreeMap
值,我们可以抽象出用于生成迭代器的方法 . 让我们添加一个特性HasValueIterator
,它知道如何获取值的迭代器:并使用它而不是
IntoIterator
:现在我们可以为
Vec
和BTreeMap
(后者使用.values()
)实现它,认为你必须命名迭代器类型:现在这适用于
Vec
和BTreeMap
,至少有值:Playground
这输出: