0

を使用して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
{ ... }

バイナリで一度に 1 つのイメージに対してこのコードを実行すると、問題なく動作します。

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、この場合にその寿命が必要な理由や修正方法がわからないことです。

おそらく問題は、一度に2つの読み取り借用からクロージャーを参照しようとしたことだと思いました(これは問題ないはずですが、必死でした)。とにかく、でラップinterpolateするRcとまったく同じエラーが発生するため、それは問題ではありませんでした。

4

1 に答える 1

1

問題は実際にはここにあります:

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

interpolateは を期待しておらず、Rust はこの&Box<Fn(u32, u32) -> Complex64 + Send + Sync>複雑さのすべてを通じて分散を処理するのがかなり苦手です。

1 つの解決策は、呼び出された場所でキャストを行うことです。

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)

そして、コンパイラーはすべてのバリアンス マジック自体を実行できます。

于 2016-08-02T11:22:51.390 に答える