簡體   English   中英

嘗試調用泛型函數時出現“預期的綁定生命周期參數”錯誤

[英]“expected bound lifetime parameter” error when attempting to call a generic function

我試圖編寫一個使用閉包驗證給定集合的函數。 該函數獲取集合的所有權,遍歷內容,如果未找到無效的項目,則返回集合的所有權。 這樣就可以像這樣使用它(無需為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函數: VecVecDequeBTreeSetBTreeMap&[T] slices

這些集合中的每一個都為自己的引用實現IntoIterator特質,可用於在引用上調用.into_iter() ,而無需使用集合中的項目:

這就是函數聲明中的for<'c> &'c C: IntoIterator<Item = V>所指的內容。 由於引用是函數主體中定義的,因此我們不能僅使用在函數中聲明的生存期(例如fn validate<'c, ... ),因為這意味着引用必須比函數壽命更長(它不能)。 取而代之的是,我們必須使用更高等級的特質綁定來聲明此生存期。

在我看來,這個生存期也是麻煩的根源,因為接受並返回對集合的引用的函數版本可以正常工作:

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

銹操場

此外,我設法實現了該函數的其他兩個版本,一個版本適用於VecVecDequeBTreeSet&[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特性。 目前,我只能將其隱含在集合或映射中,因為隱含沖突。

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

銹操場

查看您的特征范圍(重新格式化一點):

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

現在我們可以為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()
    }

}

現在,這對VecBTreeMapBTreeMap ,至少與值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.")

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM