В штучной упаковке Fn требуется статический срок службы только при тестировании?

С помощью 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`.

Я действительно не знаю, что там происходит. Я не знаю, почему я должен пометить вручную Send а также Sync в возвращаемых в штучной форме типах функций интерполяции, когда компилятор обычно получает эти черты автоматически. Тем не менее, я просто продолжал добавлять маркеры, которые предлагал компилятор, пока все не заработало.

Реальная проблема в том, что, хотя я думаю, у меня есть довольно хорошее предположение, почему вы не можете просто пометить коробочное закрытие 'staticЯ не знаю, что требует этого срока в этом случае или как это исправить.

Я предположил, что, возможно, проблема заключалась в том, что я пытался сослаться на закрытие из двух операций чтения-заимствования одновременно (что должно быть в порядке, но я был в отчаянии); во всяком случае, упаковка interpolate в Rc дает точно такую ​​же ошибку, так что это не проблема.

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)

и компилятор может делать всю магию дисперсии сама.

Другие вопросы по тегам