尝试调用通用函数时出现“预期的绑定生命周期参数”错误。

3
我将尝试编写一个使用闭包验证给定集合的函数。该函数接收一个集合,遍历其中的内容,并在未发现无效项时返回该集合的所有权。这样可以像这样使用它(而不需要为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));
}

Rust Playground

我正在为我的玩具项目编写解析器,并想知道是否可以编写一个单一的validate函数,以适用于我使用的所有集合类型: VecVecDequeBTreeSetBTreeMap&[T] slices

这些集合中的每一个都为自己的引用实现了IntoIterator trait,可以在不消耗集合中的项的情况下, 使用.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)
}

Rust Playground

此外,我还实现了另外两个版本的函数,一个适用于VecVecDequeBTreeSet&[T]切片,另一个适用于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)
}

Rust Playground

最终,我希望创建一个Validate特质。目前,我只能为集合或映射之一进行实现,因为实现会冲突。

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));
}

Rust Playground

1个回答

4

看一下你的特质边界(稍微重新格式化一下):

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`;参考往往是引用的迭代器。
修复它(以及闭包中额外的引用)可以使其工作:
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));
}

Playground

要扩展此功能以适用于BTreeMap值,我们可以抽象出用于生成迭代器的方法。让我们添加一个trait 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)
}

现在我们可以为VecBTreeMap(后者使用.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()
    }

}

现在,这适用于 VecBTreeMap,至少对于值来说是这样的:

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));
}

Playground

这将输出:

Vec: Err("[1, 2, 3, 4] contains invalid item: 4.")
Map: Err("{\"first\": 1, \"second\": 2, \"third\": 3} contains invalid item: 3.")

问题在于,BTreeMap 引用迭代器是元组引用:所以这不起作用。你所做的修复只适用于集合。不幸的是,我也设法让映射版本工作了,请参见包含 validate_collectionvalidate_mapping 的代码块。 - Nikon the Third
您想验证键和值还是仅验证值? - Chris Emerson
最好同时解决这两个问题,但如果您已经有了一个解决方案,我非常想知道您是如何做到的! - Nikon the Third
我已经更新了一种处理BTreeMap值(但不是键/值)的方法。 - Chris Emerson
谢谢,我本来以为只能用自定义特性解决,但是我认为这个方法非常适合我的需求! - Nikon the Third

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接