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.