【问题标题】:"expected bound lifetime parameter" error when attempting to call a generic function尝试调用泛型函数时出现“预期绑定的生命周期参数”错误
【发布时间】:2017-05-08 06:49:15
【问题描述】:

我正在尝试编写一个使用闭包验证给定集合的函数。该函数获取集合的所有权,遍历内容,如果没有找到无效项,则返回集合的所有权。这样就可以像这样使用它(无需为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 函数 我使用的类型: Vecs, VecDeques, BTreeSets, BTreeMaps, &[T] slices.

这些集合中的每一个都实现了IntoIterator trait 以作为自身的引用, 可用于在引用上调用 .into_iter() 而不会消耗项目 在集合中:

这就是函数声明中的for&lt;'c&gt; &amp;'c C: IntoIterator&lt;Item = V&gt; 指。由于引用是定义在函数体本身,我们不能只是 使用在函数上声明的生命周期(如fn validate&lt;'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&amp;[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)
}

Rust Playground

最后我希望创建一个Validate trait。目前,我只能实现 它用于集合或映射,因为 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));
}

Rust Playground

【问题讨论】:

    标签: rust


    【解决方案1】:

    查看您的特征界限(重新格式化):

    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 {
    

    问题是&amp;C 不会实现IntoIterator&lt;Item = V&gt;;引用倾向于迭代引用。

    修复它(以及闭包中的额外引用)使其工作:

    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 值,我们可以抽象出用于生成迭代器的方法。让我们添加一个特征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 在引用元组上引用迭代:So this doesn't work。遗憾的是,您所做的修复仅适用于收藏。我还设法获得了一个映射工作的版本,请参阅包含 validate_collectionvalidate_mapping 的代码块。
    • 您要验证键和值,还是只验证值?
    • 最好两者都有,但如果你有价值观的解决方案,我很想知道你是怎么做到的!
    • 我已经更新了一种使用 BTreeMap 值(但不是键/值)的方法。
    • 谢谢,我原以为无法绕过自定义特征。我认为这非常适合我的需求!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    • 1970-01-01
    • 2022-11-30
    • 2015-12-08
    • 2020-11-21
    相关资源
    最近更新 更多