盒装Fn只在测试时才需要生命周期?

时间:2016-08-01 17:07:58

标签: closures rust lifetime-scoping

使用rustc 1.10.0,我试图编写一些绕过盒装闭包的代码 - 最终的目标是在程序上生成分形动画。现在我有一些像这样的功能签名:

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
{ ... }

在二进制文件中一次运行一个图像的代码没有问题:

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);

但是,我想确保我的串行和并行图像生成都产生相同的结果,所以我编写了以下测试函数:

#[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));
}

这拒绝编译,我无法弄明白。它给出的错误如下:

> 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`.

我真的不知道那里发生了什么。当编译器通常自动导出这些特征时,我不知道为什么我需要在插值函数的盒装返回类型中手动标记SendSync。尽管如此,我还是不断添加编译器建议的标记,直到工作正常。

真正的问题是,虽然我觉得我很清楚为什么你不能只标记盒装封闭'static,但我不知道需要什么在这种情况下的生命周期或如何解决它。

我猜测可能问题是我试图立刻从两个读取借用中引用闭包(这应该没问题,但我很绝望);无论如何,在interpolate中包裹Rc会产生完全相同的错误,因此问题并非如此。

1 个答案:

答案 0 :(得分:1)

问题实际上在这里:

pub fn sequential_image<F>(
    ...,
    interpolate: &Box<Fn(u32, u32) -> Complex64>,
    ...) -> ...

interpolate不期望&Box<Fn(u32, u32) -> Complex64 + Send + Sync>,并且Rust在处理所有这些复杂性的差异方面非常糟糕。

一种解决方案是在所谓的演员阵容中进行演绎:

sequential_image(width, height, &default_julia,
    &(interpolate as Box<Fn(u32, u32) -> Complex64>),
threshold)

但这需要一个sequential_image的值案例并且非常难看。

更好的方法是将sequential_image的参数修复为更通用的内容,以及编译器更容易理解的内容:基本指针。

pub fn sequential_image<F>(
    ...,
    interpolate: &Fn(u32, u32) -> Complex64,
    ...) -> ...

现在你可以用

来调用它
sequential_image(width, height, &default_julia,
    &*interpolate,
threshold)

并且编译器可以自己完成所有方差魔法。