代码之家  ›  专栏  ›  技术社区  ›  Callum Rogers

使用map_键在对上扩展迭代器

  •  2
  • Callum Rogers  · 技术社区  · 6 年前

    我想写一些简单的铁锈扩展方法 Iterator 当它重复 (K, V) 对。对于映射键,我能想到的最简单的实现包括重用 Iterator::map 像这样:

    use std::iter::Map;
    
    trait KeyedIterator<K, V>: Iterator<Item = (K, V)> {
        fn map_keys<R, F, G>(self, f: F) -> Map<Self, G>
        where
            Self: Sized,
            F: FnMut(K) -> R,
            G: FnMut((K, V)) -> (R, V),
        {
            self.map(|(key, value): (K, V)| (f(key), value))
        }
    }
    
    impl<I, K, V> KeyedIterator<K, V> for I where I: Iterator<Item = (K, V)> {}
    

    但是,它有以下错误:

    error[E0308]: mismatched types
      --> src/lib.rs:10:18
       |
    10 |         self.map(|(key, value): (K, V)| (f(key), value))
       |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found closure
       |
       = note: expected type `G`
                  found type `[closure@src/lib.rs:10:18: 10:56 f:_]`
    

    闭包不应该实现 G 因为它是一个函数 (k,v) (R, V) ?我这里缺什么?

    2 回复  |  直到 6 年前
        1
  •  4
  •   Peter Hall    6 年前

    如果为类型或函数声明类型参数,则该类型必须由 呼叫者 . 但是,在代码中,您正试图确定类型 G 体内 map_keys ,基于在其中定义的闭包类型。

    通常,函数体确定类型的方法是使用存在主义返回类型(例如 Map<Self, impl FnMut((K, V)) -> (R, V)> . 然而,这在特征方法中是不允许的。

    但是,用于所有内置迭代器适配器的模式将适用于您的用例。也就是说,定义一个由方法返回的结构,并使其成为迭代器:

    // Struct to hold the state of the iterator
    struct KeyedIter<I, F> {
        iter: I,
        f: F,
    }
    
    // Make KeyedIter an iterator whenever `I` is an iterator over tuples and `F` has the 
    // correct signature
    impl<K, V, R, I, F> Iterator for KeyedIter<I, F>
    where
        I: Iterator<Item = (K, V)>,
        F: FnMut(K) -> R,
    {
        type Item = R;
        fn next(&mut self) -> Option<Self::Item> {
            self.iter.next().map(|(k, _v)| (self.f)(k))
        }
    }
    
    // A trait for adding the `map_keys` method
    trait KeyedIterator<K, V> {
        fn map_keys<R, F>(self, f: F) -> KeyedIter<Self, F>
        where
            F: FnMut(K) -> R,
            Self: Sized;
    }
    
    // implement the trait for all iterators over tuples
    impl<I, K, V> KeyedIterator<K, V> for I
    where
        I: Iterator<Item = (K, V)>,
    {
        fn map_keys<R, F>(self, f: F) -> KeyedIter<Self, F>
        where
            F: FnMut(K) -> R,
            Self: Sized,
        {
            KeyedIter { iter: self, f }
        }
    }
    

    这个 KeyedIter 结构由调用方知道的类型参数化:前一个迭代器和映射函数。不需要尝试表示中间闭包的类型,而是在迭代器的 next() 方法。


    参见:

        2
  •  0
  •   Caio    6 年前

    G 由方法和抽象绑定在为每个函数调用传入的具体类型上。例如:

    fn print<T: core::fmt::Debug>(t: T) {
        println!("{:?}", t);
    }
    
    fn main() {
        print(1);
        print(1f64);
        print("1");
    }
    

    这意味着不可能返回任意 固定的 G 但有一些解决方法。

    1-静态调度。必须修改代码以接收和返回相同的泛型:

    use core::iter::Map;
    
    trait KeyedIterator<K, V>: Iterator<Item = (K, V)> {
        fn map_keys<R, F>(self, f: F) -> Map<Self, F>
        where
            Self: Sized,
            F: FnMut((K, V)) -> (R, V),
        {
            self.map(f)
        }
    }
    
    impl<I, K, V> KeyedIterator<K, V> for I where I: Iterator<Item = (K, V)> {}
    
    fn main() {
        let vec = vec![(1u32, 2i32), (3, 4), (5, 6)];
        println!("{:?}", vec.into_iter().map_keys(|(k, v)| (k as f64 + 0.8, v)).collect::<Vec<(f64, i32)>>());
    }
    

    2-动态调度。只需一点运行时开销,就可以使用 Box .

    trait KeyedIterator<K, V>: Iterator<Item = (K, V)> {
        fn map_keys<'a, R, F: 'a>(self, mut f: F) -> Box<Iterator<Item = (R, V)> + 'a>
        where
            Self: Sized + 'a,
            F: FnMut(K) -> R
        {
            Box::new(self.map(move |(key, value): (K, V)| (f(key), value)))
        }
    }
    
    impl<I, K, V> KeyedIterator<K, V> for I where
        I: Iterator<Item = (K, V)> {}
    
    fn main() {
        let vec = vec![(1u32, 2i32), (3, 4), (5, 6)];
        println!(
            "{:?}",
            vec.into_iter()
                .map_keys(|k| k as f64 + 0.8)
                .collect::<Vec<(f64, i32)>>()
        );
    }