Michael Mior
Michael Mior

Reputation: 28753

Rayon support for itertools tuple_combinations

I'm trying to use Rayon's par_iter over a TupleCombinations struct from itertools as in the code below.

use itertools::Itertools;
use rayon::prelude::*;

fn main() {
    let v: Vec<i32> = vec!(1, 2, 3);
    let t = v.iter().tuple_combinations::<(_, _)>();
    let sum: i32 = t.par_iter().map(|&(i, j)| i * j).sum();
    println!("{}", sum);
}

The problem is that Rayon seems to expect something which implements the IntoIterator trait, but tuple_combinations returns a TupleCombinations struct which implements Iterator. I end up with the error below. I believe the solution is to implement IntoParallelIterator for TupleCombinations but it seems like this isn't possible to do since there are some private trait bounds that must be satisfied. I thought I would see if anyone

error[E0599]: the method `par_iter` exists for struct `TupleCombinations<std::slice::Iter<'_, i32>, (&i32, &i32)>`, but its trait bounds were not satisfied
   --> src/main.rs:8:22
    |
8   |     let sum: i32 = t.par_iter().map(|&(i, j)| i * j).sum();
    |                      ^^^^^^^^ method cannot be called on `TupleCombinations<std::slice::Iter<'_, i32>, (&i32, &i32)>` due to unsatisfied trait bounds
    |
   ::: /home/mmior/.cargo/registry/src/github.com-1ecc6299db9ec823/itertools-0.10.3/src/adaptors/mod.rs:701:1
    |
701 | pub struct TupleCombinations<I, T>
    | ---------------------------------- doesn't satisfy `_: rayon::iter::IntoParallelRefIterator`
    |
    = note: the following trait bounds were not satisfied:
            `&TupleCombinations<std::slice::Iter<'_, i32>, (&i32, &i32)>: IntoParallelIterator`
            which is required by `TupleCombinations<std::slice::Iter<'_, i32>, (&i32, &i32)>: rayon::iter::IntoParallelRefIterator`

I started trying to implement this using a wrapper struct as follows:

use itertools::{Itertools, TupleCombinations};
use rayon::prelude::*;

struct MyPairGenerator {
    values: Vec<i32>,
}

//TupleCombinations<std::slice::Iter<'_, i32>, (&i32, &i32)>
impl IntoIterator for MyPairGenerator {
    type Item = (i32, i32);
    type IntoIter = TupleCombinations<std::vec::IntoIter<i32>, (i32, i32)>;

    fn into_iter(self) -> Self::IntoIter {
        self.values.into_iter().tuple_combinations::<(i32, i32)>()
    }
}

fn main() {
    let v: Vec<i32> = vec!(1, 2, 3);
    let t = MyPairGenerator { values: v };
    let sum = t.par_iter().map(|&(i, j)| i * j).sum();
    println!("{}", sum);
}

It seems like now I should be able to implement IntoParallelIterator for MyPairGenerator, but I'm stuck as to where to start. Rayon seems to use private macros to do a lot of the work.

Upvotes: 1

Views: 1271

Answers (1)

Chayim Friedman
Chayim Friedman

Reputation: 70870

Any iterator can be converted into a parallel iterator by calling par_bridge(). However, you may be able to implement ParallelIterator more efficiently.

Upvotes: 2

Related Questions