В штучной упаковке 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)
и компилятор может делать всю магию дисперсии сама.