为什么 filter() 对并行迭代器有不同的类型要求?

问题描述

我试图理解为什么 Rayon 的 filter() 函数在不需要指定正确类型的情况下就无法工作,而 filter() 如果我不使用并行迭代器则可以正常工作。这是我的代码

use rayon::prelude::*;

fn is_even(n: i64) -> bool {
    n % 2 == 0
}

fn main() {
    let v: Vec<_> = (1..300_000_000)
        .into_par_iter()  //  works correctly without this line,but not parallel
        .filter(|&x| is_even(x))
        .collect();
}

这里是错误信息:

error[E0271]: type mismatch resolving `<rayon::range::Iter<i32> as rayon::iter::ParallelIterator>::Item == i64`
  --> src/main.rs:11:10
   |
11 |         .filter(|&x| is_even(x))
   |          ^^^^^^ expected `i32`,found `i64`

error[E0271]: type mismatch resolving `<rayon::range::Iter<i32> as rayon::iter::ParallelIterator>::Item == i64`
  --> src/main.rs:12:10
   |
12 |         .collect();
   |          ^^^^^^^ expected `i32`,found `i64`
   |
   = note: required because of the requirements on the impl of `rayon::iter::ParallelIterator` for `rayon::iter::Filter<rayon::range::Iter<i32>,[closure@src/main.rs:11:17: 11:32]>`

如果我不使用 filter(),为什么 into_par_iter() 只能在不指定整数类型的情况下工作? (我知道我可以通过将范围标记为 i64 来修复它,但不知道为什么有必要)

解决方法

更新: 此类型推断问题已在 rayon 1.5.1 中fixed


为什么...深入研究,这是由于人造丝确定 Range 是否实现 IntoParallelIterator 的方式。

impl<T> IntoParallelIterator for Range<T> where Iter<T>: ParallelIterator { ... }

struct Iter<T> {
    range: Range<T>,}

impl ParallelIterator for Iter<u8> { type Item = u8; }
impl ParallelIterator for Iter<u16> { type Item = u16; }
impl ParallelIterator for Iter<u32> { type Item = u32; }
impl ParallelIterator for Iter<u64> { type Item = u64; }
impl ParallelIterator for Iter<i8> { type Item = i8; }
impl ParallelIterator for Iter<i16> { type Item = i16; }
impl ParallelIterator for Iter<i32> { type Item = i32; }
impl ParallelIterator for Iter<i64> { type Item = i64; }
// etc

编译器试图查看 (1..300_000_000).into_par_iter() 是否合法,并且因为 ParallelIterator 是单独为 Iter<T> 类型实现的,所以它被迫现在推断出什么T 是在它继续之前。

查看 playground 上的非工作重建。

如果他们做了类似的事情:

impl<T> ParallelIterator for Iter<T> where T: SomeIntegerType + Send {
    type Item = T;
}

trait SomeIntegerType {}
impl SomeIntegerType for u8 {}
impl SomeIntegerType for u16 {}
impl SomeIntegerType for u32 {}
impl SomeIntegerType for u64 {}
impl SomeIntegerType for i8 {}
impl SomeIntegerType for i16 {}
impl SomeIntegerType for i32 {}
impl SomeIntegerType for i64 {}
// etc

编译器可以看到 Iter 确实实现了 ParallelIterator,只要 T 实现了 SomeIntegerType,但它不必推断类型现在,它可以等到以后。

查看我在 playground 上的工作重建。