Using rustc 1.10.0
, I'm trying to write some code which passes around boxed closures--the eventual goal is to procedurally generate an animation of fractals. Right now I have some function signatures like this:
pub fn interpolate_rectilinear(width: u32, height: u32, mut min_x: f64, mut max_x: f64, mut min_y: f64, mut max_y: f64)
-> Box<Fn(u32, u32) -> Complex64 + Send + Sync + 'static> { ... }
pub fn interpolate_stretch(width: u32, height: u32, mut min_x: f64, mut max_x: f64, mut min_y: f64, mut max_y: f64)
-> Box<Fn(u32, u32) -> Complex64 + Send + Sync + 'static> { ... }
pub fn parallel_image<F>(width: u32, height: u32, function: &F, interpolate: &Box<Fn(u32, u32) -> Complex64 + Send + Sync>, threshold: f64)
-> ImageBuffer<image::Luma<u8>, Vec<u8>>
where F: Sync + Fn(Complex64) -> Complex64
{ ... }
pub fn sequential_image<F>(width: u32, height: u32, function: &F, interpolate: &Box<Fn(u32, u32) -> Complex64>, threshold: f64)
-> ImageBuffer<image::Luma<u8>, Vec<u8>>
where F: Fn(Complex64) -> Complex64
{ ... }
Running this code for one image at a time in a binary works without problems:
let interpolate = interpolate_rectilinear(width, height, -1.0, 1.0, -1.0, 1.0);
let image = parallel_image(width * 2, height * 2, &default_julia, &interpolate, 2.0);
However, I wanted to ensure my serial and parallel image-production were both producing the same results, so I wrote the following test function:
#[test]
fn test_serial_parallel_agree() {
let (width, height) = (200, 200);
let threshold = 2.0;
let interpolate = interpolate_stretch(width, height, -1.0, 1.0, -1.0, 1.0);
assert!(parallel_image(width, height, &default_julia, &interpolate, threshold)
.pixels()
.zip(sequential_image(width, height, &default_julia, &interpolate, threshold)
.pixels())
.all(|(p, s)| p == s));
}
This refuses to compile, and I just can't figure it out. The error it gives is as follows:
> cargo test
Compiling julia-set v0.3.0
src/lib.rs:231:66: 231:78 error: mismatched types [E0308]
src/lib.rs:231 .zip(sequential_image(width, height, &default_julia, &interpolate, threshold)
^~~~~~~~~~~~
src/lib.rs:229:9: 233:36 note: in this expansion of assert! (defined in <std macros>)
src/lib.rs:231:66: 231:78 help: run `rustc --explain E0308` to see a detailed explanation
src/lib.rs:231:66: 231:78 note: expected type `&Box<std::ops::Fn(u32, u32) -> num::Complex<f64> + 'static>`
src/lib.rs:231:66: 231:78 note: found type `&Box<std::ops::Fn(u32, u32) -> num::Complex<f64> + Send + Sync>`
error: aborting due to previous error
Build failed, waiting for other jobs to finish...
error: Could not compile `julia-set`.
I really don't know what's going on there. I don't know why I'm required to manually mark Send
and Sync
in the boxed return types of the interpolation functions, when the compiler typically derives those traits automatically. Still, I just kept adding in markers that the compiler suggested until things worked.
The real problem is that, while I think I have a pretty good guess why you can't just mark a boxed closure 'static
, I don't know what's requiring that lifetime in this case or how to fix it.
I did guess that possibly the issue was that I was trying to reference the closure from two read-borrows at once, (which should be ok, but I was desperate); at any rate, wrapping interpolate
in an Rc
gives the exact same error, so that wasn't the problem.