2016-09-08 1 views
3

Es gibt bereits viele Themen zu diesem Thema, aber ich sehe nicht, ob die besprochenen Probleme auf mein spezifisches Problem zutreffen.Funktionsreferenzen: erwarteter gebundener Lebensdauerparameter, gefundene konkrete Lebensdauer [E0271]

Ich habe eine Struktur, die eine name und eine callback Funktion speichert. Abgespeckte auf das Problem sieht es wie folgt aus: (. Und prob mehr Sachen in der Zukunft)

pub struct Command<'a> { 
    name: &'a str, 
    callback: &'a Fn(&[&str]) ->() 
} 

impl <'a> Command<'a> { 
    pub fn new(name: &'a str, callback: &'a Fn(&[&str]) ->()) -> Command<'a> { 
     Command { 
      name: name, 
      callback: callback 
     } 
    } 
} 

Was ich tun möchte, ist speichern Sie eine Callback-Funktion mit einem Namen verbunden.

Aber wenn ich versuche, dieses Stück Code zu verwenden, etwa so:

fn main() { 
    let play_callback = |args| { 
     println!("Playing something."); 
     for arg in args { 
      println!("{}", arg); 
     } 
    }; 
    let play_command = Command::new("play", &play_callback); 
} 

bekomme ich folgende Fehlermeldung:

src/main.rs:22:42: 22:56 error: type mismatch resolving `for<'r, 'r> <[[email protected]/main.rs:16:22: 21:3] as std::ops::FnOnce<(&'r [&'r str],)>>::Output ==()`: 
expected bound lifetime parameter , 
    found concrete lifetime [E0271] 
src/main.rs:22 let play_command = Command::new("play", &play_callback); 
                 ^~~~~~~~~~~~~~ 

Ich habe versucht, die Schließung wie diese

fn main() { 
    let play_command = Command::new("play", &|args| { 
     println!("Playing something."); 
     for arg in args { 
      println!("{}", arg); 
     } 
    }); 
} 
Inline

aber dann bekomme ich einen anderen Fehler

src/main.rs:16:47: 21:7 error: borrowed value does not live long enough 

was ich glaube ich verstehe, warum ich bekomme.

Ich habe versucht, einen generischen Typparameter für Command Verwendung vor dem ersten zu einer Funktionsreferenz Schalt in meiner Command Struktur zu speichern, aber wenn ich einen HashSet von Befehlsobjekten wie folgt initialisieren wollte:

let mut commands: HashSet<Command> = HashSet::new(); 

der Compiler wollte, dass ich den generischen Parameter spezifiziere, von dem ich glaube, dass ich das nicht tun kann, dass das bedeuten würde, dass ich nur die gleiche Schließung in allen meinen Command Objekten speichern könnte.

Also meine Frage wäre: Wie kann ich erreichen, was ich will und was ist der beste Weg (und warum)?

Antwort

4

Die einfache Lösung (von ljedrz) ist, dass in diesem Fall nicht abgeleitet wird. Es kann jedoch nicht ausreichen, um Ihr Problem zu lösen.

Wenn Sie einen Verweis auf ein Merkmal als Funktionsargument verwenden, wird dies als Merkmalsobjekt behandelt. In diesem Fall ist das &Fn ein Objektmerkmal, das auf den Abschluss auf dem Stapel verweist.

Eine einfache Analogie von Merkmalsobjekten sind Objekte, die Interfaces in anderen Sprachen implementieren.

Lebensdauern funktionieren jedoch ein bisschen anders mit den Merkmalobjekten. Sie können sich diese als "losgelöst" von dem üblichen Eigentumsfluss vorstellen.Wenn wir die Fn Merkmal Objektlebensdauer 'c in Ihrem Beispiel zu annotieren, würden wir den folgenden Code erhalten:

pub struct Command<'a> { 
    name: &'a str, 
    callback: &'a for<'c> Fn(&'c [&'c str]) ->() 
} 

impl <'a> Command<'a> { 
    pub fn new<'r>(name: &'r str, callback: &'r for<'c> Fn(&'c [&'c str]) ->()) -> Command<'r> { 
     Command { 
      name: name, 
      callback: callback 
     } 
    } 
} 

fn main() { 
    let play_callback = |args: &[&str]| { 
     println!("Playing something."); 
     for arg in args { 
      println!("{}", arg); 
     } 
    }; 
    let play_command = Command::new("play", &play_callback); 
} 

In dem obigen Code beschreibt die Lebensdauer 'c einen Bereich, in dem die Callback-Funktion aufgerufen werden.

Der obige Code ist jedoch nicht sehr praktisch. Sie koppelt den Befehl an den Bereich, in dem die Closure erstellt wurde (denken Sie daran, dass das Objektobjekt auf die Closure in diesem Bereich verweist). Sie können also nicht von der Funktion, in der Ihre Command erstellt wurde, ausgehen, denn das würde die Schließung zerstören!

Die wahrscheinlichste Lösung ist, den Verschluss auf dem Haufen zu speichern, in Box<Fn(&[&str])>. Die Lebensdauer eines Merkmalsobjekts in der Box (Heapspeicher) wird durch die Erstellung und Zerstörung der Box gesteuert, so dass es möglichst breit ist ('static).

pub struct Command<'a> { 
    name: &'a str, 
    callback: Box<Fn(&[&str]) ->()> 
} 

impl <'a> Command<'a> { 
    pub fn new<'r>(name: &'r str, callback: Box<Fn(&[&str]) ->()>) -> Command<'r> { 
     Command { 
      name: name, 
      callback: callback 
     } 
    } 
} 

fn main() { 
    let play_callback = |args: &[&str]| { 
     println!("Playing something."); 
     for arg in args { 
      println!("{}", arg); 
     } 
    }; 
    let play_command = Command::new("play", Box::new(play_callback)); 
} 

In dem obigen Beispiel wird der Verschluß in die Box bewegt werden, wenn das Feld angelegt wird, und wird zusammen mit Command zerstört werden.

+0

Das hilft viel, danke. –

2

Haben Sie versucht, den Typ args anzugeben? Die folgenden Kompilate für mich:

fn main() { 
    let play_callback = |args: &[&str]| { 
     println!("Playing something."); 
     for arg in args { 
      println!("{}", arg); 
     } 
    }; 
    let play_command = Command::new("play", &play_callback); 
} 

Ich weiß nicht, warum es nicht abgeleitet ist, obwohl.

Verwandte Themen