2

I have a Vec of boxed closures (Vec<Box<Fn(...) -> ...>>). I can do vec.into_iter().map(...), but I can't use it with Rayon's into_par_iter vec.into_par_iter().map(...).

Here's a minimised example (playground):

type GameResult<T> = Result<T, ()>;
type Game = ();
type PlayerId = String;

use rayon::prelude::*; // 1.5.1

fn main() {
    // the type of our closures. It's boxed, so it's sized right?
    type CpuOption = Box<dyn Fn(u32) -> GameResult<u32>>;
    let mut options: Vec<CpuOption> = vec![];

    options.push(Box::new(|i| Ok(i + 2)));
    options.push(Box::new(|i| Ok(i + 3)));

    let try_it = |option: CpuOption| -> GameResult<u32> { Ok(option(12)?) };

    let parallel = true;

    if parallel {
        // ERROR IS HERE. says my type isn't sized?
        let options = options
            .into_par_iter()
            .map(try_it)
            .flat_map_iter(Result::into_iter);
        let best_option = options.max_by_key(|score| *score).unwrap();
    } else {
        // but this works fine!
        let options = options.into_iter().map(try_it).flat_map(Result::into_iter);
        let best_option = options.max_by_key(|score| *score).unwrap();
    }
}

The error that I get:

   Compiling playground v0.0.1 (/playground)
error[E0599]: the method `into_par_iter` exists for struct `Vec<Box<(dyn Fn(u32) -> Result<u32, ()> + 'static)>>`, but its trait bounds were not satisfied
  --> src/lib.rs:24:14
   |
24 |             .into_par_iter()
   |              ^^^^^^^^^^^^^ method cannot be called on `Vec<Box<(dyn Fn(u32) -> Result<u32, ()> + 'static)>>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `[Box<dyn Fn(u32) -> Result<u32, ()>>]: Sized`
           which is required by `[Box<dyn Fn(u32) -> Result<u32, ()>>]: rayon::iter::IntoParallelIterator`
           `[Box<dyn Fn(u32) -> Result<u32, ()>>]: rayon::iter::ParallelIterator`
           which is required by `[Box<dyn Fn(u32) -> Result<u32, ()>>]: rayon::iter::IntoParallelIterator`

I was under the impression that wrapping the closure in the box was the solution to making closures be sized so I've done that here.

If I read the error right, [Box<dyn Fn(u32) -> Result<u32, ()>>]: Sized isn't met and it's a prerequisite for IntoParallelIterator which is the piece I'm after. The regular into_iter() works fine so there's some additional constraint that Rayon is placing. I expected Rayon to have more constraints but I assumed that it would be something about Send

I know in this minimal example I could use fn(...) types instead of Fn(...) types because they don't capture any values but of course my real code does capture references.

I'm using Rayon to write the AI for a little game using a minimax style where I have a list of things for the AI to try and I then observe the score after each of those things. I want the top-level of this recursive call stack to start the top level in many threads but each thread should be sequential.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
david king
  • 738
  • 5
  • 9
  • `[AnyPossibleType]` is not sized. – Shepmaster Aug 11 '21 at 17:44
  • @Shepmaster where is that type coming from though? I don't have any raw array types in my code and I assume Rayon works so it's not just a fundamental thing that Rayon makes raw array types so can't ever work. – david king Aug 11 '21 at 17:46
  • That's not an array; arrays are `[T; N]`. It is a slice (compared to a slice reference `&[T]` or a boxed slice `Box<[T]>`). – Shepmaster Aug 11 '21 at 17:47
  • 1
    [The duplicates applied to your situation](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=11cb98157986bc989b2f57d10764daae). Specifically: `type CpuOption = Box GameResult + Send>;`. TL;DR your closures cannot be sent between threads and [`impl IntoParallelIterator for Vec`](https://docs.rs/rayon/1.5.1/rayon/iter/trait.IntoParallelIterator.html#impl-IntoParallelIterator-for-Vec%3CT%3E). – Shepmaster Aug 11 '21 at 17:49
  • 1
    It is strange that `IntoParallelIterator::into_par_iter(options);` gives you a better error message but when you call `options.into_par_iter` probably compiler tried to find the closest implementation without `Send` – Ömer Erden Aug 11 '21 at 17:54
  • 1
    @Shepmaster If the problem is just in the missing `Send` bound, why is the compiler complaining about missing `Sized`? Is that a flaw in diagnostics or a weird interaction with some other language feature that throws off the compiler (and would make the diagnostic legitimate in other situations)? – user4815162342 Aug 11 '21 at 18:00
  • @user4815162342 I'd guess it's close to what Ömer Erden said — the compiler has a giant list of possible types that implement the required traits and it has to decide which of those to include in the error message. I'm guessing that `[T]` counts as "getting closer" and is usually the better thing to show. I'd say that it is indeed a diagnostics issue, but I shudder to think how the compiler could be changed to do better... – Shepmaster Aug 11 '21 at 18:29

0 Answers0