2016-08-01 12 views
0

Unter Verwendung von rustc 1.10.0 versuche ich, Code zu schreiben, der verklemmte Schließungen passiert - das letztendliche Ziel ist es, eine Animation von Fraktalen prozedural zu generieren. Im Moment habe ich einige Funktionssignaturen wie folgt aus:Boxed Fn benötigt Lebenszeit nur beim Testen statisch?

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

Ausführen dieses Code für ein Bild zu einem Zeitpunkt in einem binären Arbeiten ohne Probleme:

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

Aber ich wollte meine serielle und parallele, um sicherzustellen, Bildproduktion waren beide die gleichen Ergebnisse zu erzielen, so schrieb ich die folgende Testfunktion:

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

Dieser weigert sich zu kompilieren, und ich kann es einfach nicht herausfinden. Der Fehler ist wie folgt:

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

Ich weiß wirklich nicht, was dort vor sich geht. Ich weiß nicht, warum ich Send und Sync in den Boxed-Return-Typen der Interpolationsfunktionen manuell markieren muss, wenn der Compiler diese Merkmale normalerweise automatisch ableitet. Immerhin fügte ich einfach Marker hinzu, die der Compiler vorgeschlagen hatte, bis die Dinge funktionierten.

Das eigentliche Problem ist, dass, während ich denke, ich habe eine ziemlich gute Vermutung, warum Sie nicht einfach einen verschlossenen Verschluss 'static markieren, weiß ich nicht, was diese Lebensdauer in diesem Fall oder wie es zu beheben erfordert.

Ich vermutete, dass möglicherweise das Problem war, dass ich versuchte, die Schließung von zwei Read-borrows auf einmal zu referenzieren (was in Ordnung sein sollte, aber ich war verzweifelt); Auf jeden Fall gibt interpolate in einem Rc den gleichen Fehler, so dass das Problem nicht war.

+0

Haben Sie ein Git Repo oder eine solche? Ich würde das gern testen. – Veedrac

+0

Zusätzlich zu Veedracs Kommentar solltest du einen [MCVE] angeben, wenn du hier eine Frage zu Stack Overflow stellst (wirklich überall, aber besonders hier). Wenn wir Ihr Problem nicht ausschließlich von dem, was hier enthalten ist, reproduzieren können, wird die Frage geschlossen. – Shepmaster

+0

@Veedrac Ja, das Repository ist [hier] (https: // github.com/coriolinus/julia-set/baum/c3b1d31ec9bc981f1a0575c7e3fcc2d377de30db). Der Ort, an dem der Anruf funktioniert, ist [hier] (https://github.com/coriolinus/julia-set/blob/master/src/bin.rs#L79-L82), und der Test, bei dem es nicht funktioniert, ist [hier] (https://github.com/coriolinus/julia-set/blob/master/src/lib.rs#L223-L234). Ich hatte so wenig eingefügt wie hier, weil es so aussah, als sei die Frage schon ziemlich lang und detailliert, und es wäre nicht minimal gewesen, mehrere hundert Zeilen überflüssiger Quellen einzufügen. – coriolinus

Antwort

1

Das Problem ist eigentlich hier:

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

Die interpolate keine &Box<Fn(u32, u32) -> Complex64 + Send + Sync> erwarten, und Rust ist ziemlich schlecht Varianz durch all diese Komplexität bei der Handhabung.

Eine Lösung ist, die Besetzung zu tun, wo es heißt:

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

aber dies erfordert einen Wert Fall von sequential_image und ist verdammt hässlich.

Ein netterer Weg ist es, den Parameter sequential_image auf etwas allgemeiner und etwas einfacher für den Compiler zu beheben, um über: grundlegende Zeiger.

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

Jetzt können Sie es mit

einfach anrufen
sequential_image(width, height, &default_julia, 
    &*interpolate, 
threshold) 

und der Compiler kann sich die gesamte Varianz Magie tun.

+0

Der Grund, warum ich die "Box " Rabbithole in erster Linie gestartet. Wenn ich die Signatur von 'interpolate' ändere, wie Sie vorschlagen, beklagt' parallel_image', dass 'Fn (u32, u32) -> Complex64 nicht sicher zwischen Threads geteilt werden kann. Ich versuche, das zu lösen, indem ich die Funktion in einen Arc verpacke: "interpoliere = Arc :: new (interpoliere);", aber das ändert den Fehler nicht. Wenn ich es stattdessen als 'let interpolate = Arc :: new (* interpolate);' umwandle, wird der Fehler zu 'dem Merkmal gebunden std :: ops :: Fn (u32, u32) -> num :: Komplex : std: : marker :: Sized ist nicht zufrieden'. Wie sonst könnte ich das angehen? – coriolinus

+0

Ich möchte, dass 'sequential_image' und' parallel_image' die gleiche Signatur teilen, da es für den Aufrufer keine Rolle spielt, was unter der Haube passiert. – coriolinus

+0

@coriolinus 'parallel_image' muss nur' interpolieren: & (Fn (u32, u32) -> Complex64 + Senden + Sync) '. – Veedrac