我正在尝试编写一个使用闭包来验证给定集合的函数。该函数获取集合的所有权,遍历内容,如果未找到无效项,则返回集合的所有权。这样就可以像这样使用(不为Vec
创建临时值):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>
是指。由于引用是在函数体本身 中定义的,所以我们不能
使用在函数上声明的生命周期(如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
特征。目前,我只能impl
它适用于集合或映射,因为冲突会发生冲突。
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));
}
答案 0 :(得分:3)
观察你的特质界限(重新格式化一点):
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 {
问题是&C
无法实施IntoIterator<Item = V>
;引用倾向于迭代引用。
修复(以及闭包中的额外引用)使其工作:
fn validate<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)
}
fn main() {
println!("Vec: {:?}", validate(vec![1, 2, 3, 4], |&v| v <= 3));
}
为了扩展它以使用BTreeMap
值,我们可以抽象出用于生成迭代器的方法。让我们添加一个特性HasValueIterator
,它知道如何在值上获取迭代器:
trait HasValueIterator<'a, V: 'a> {
type ValueIter : Iterator<Item=&'a V>;
fn to_value_iter(&'a self) -> Self::ValueIter;
}
并使用它代替IntoIterator
:
fn validate<C, F, V>(col: C, pred: F) -> Result<C, String>
where C: Debug,
for<'c> C: HasValueIterator<'c, V>,
F: Fn(&V) -> bool,
V: Debug
{
if let Some(val) = (&col).to_value_iter().find(|v| !pred(v)) {
Err(format!("{:?} contains invalid item: {:?}.", col, val))?;
}
Ok(col)
}
现在我们可以为Vec
和BTreeMap
(后者使用.values()
)实现它,认为你必须命名迭代器类型:
impl<'c, V:'c> HasValueIterator<'c, V> for Vec<V> {
type ValueIter = std::slice::Iter<'c,V>;
fn to_value_iter(&'c self) -> Self::ValueIter {
self.iter()
}
}
impl<'c, V:'c, K:'c> HasValueIterator<'c, V> for BTreeMap<K, V> {
type ValueIter = std::collections::btree_map::Values<'c, K, V>;
fn to_value_iter(&'c self) -> Self::ValueIter {
self.values()
}
}
现在,这适用于Vec
和BTreeMap
,至少包含值:
fn main() {
println!("Vec: {:?}", validate(vec![1, 2, 3, 4], |&v| v <= 3));
let mut map = BTreeMap::new();
map.insert("first", 1);
map.insert("second", 2);
map.insert("third", 3);
println!("Map: {:?}", validate(map, |&v| v<=2));
}
输出:
Vec: Err("[1, 2, 3, 4] contains invalid item: 4.")
Map: Err("{\"first\": 1, \"second\": 2, \"third\": 3} contains invalid item: 3.")