2017-03-05 24 views
0

Hallo Ich habe Probleme mit Lebenszeiten in einem ziemlich einfachen Testfall und ich kann nicht scheinen zu verstehen, was der Compiler mir sagt. Der folgende Code fügt in Rust Spielplatz ein.Lifetime Errors Ich weiß nicht, wie ich lösen soll

Die Idee über den Code ist, dass Element s in einer Pipeline aufgereiht sind. Daten werden schließlich von einem Element zum nächsten weitergegeben. Jede Stufe hat ein (rx, tx)-Paar, auf dem sie Eingaben von der vorherigen Stufe empfängt und Daten an die nächste Stufe sendet. Ich kann die Element s als Sync markieren, da nur ein Element jemals einen Teil der Daten auf einmal verarbeiten wird.

Die Fehler sind:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements 
    --> <anon>:56:18 
    | 
56 |   for e in self.elements { 
    |     ^^^^^^^^^^^^^ 
    | 
note: first, the lifetime cannot outlive the lifetime 'a as defined on the body at 53:53... 
    --> <anon>:53:54 
    | 
53 |  pub fn run(&self) -> Vec<thread::JoinHandle<()>> { 
    |             ^
note: ...so that expression is assignable (expected std::vec::Vec<Box<&Element>>, found std::vec::Vec<Box<&'a Element + 'a>>) 
    --> <anon>:56:18 
    | 
56 |   for e in self.elements { 
    |     ^^^^^^^^^^^^^ 
    = note: but, the lifetime must be valid for the static lifetime... 
note: ...so that the type `[[email protected]<anon>:62:41: 64:15 e:Box<&Element>, sender:std::sync::Arc<std::sync::Mutex<std::sync::mpsc::SyncSender<(std::string::String, std::string::String)>>>, receiver:std::sync::Arc<std::sync::Mutex<std::sync::mpsc::Receiver<(std::string::String, std::string::String)>>>]` will meet its required lifetime bounds 
    --> <anon>:62:27 
    | 
62 |    handles.push(thread::spawn(move || { 
    |       ^^^^^^^^^^^^^ 

Der erste Fehler, den ich durch bin verwirrt als Element s als &'a Element definiert sind, so sollten sie den Compiler nicht sagen, dass sie für zusammen als Pipeline um bleiben?

Der zweite Fehler, den ich schätze, sagt mir Vec<thread::JoinHandle<()>> als gesagt zu werden, hängt es von der Lebenszeit ab 'a? Ich bin mir nicht sicher, wie ich das vermitteln soll.

Ich hoffe, der dritte wird mehr Sinn machen, nachdem ich die ersten beiden korrigieren. Im Moment weiß ich einfach nicht, was es mir sagt.

use std::sync::{Arc, Mutex}; 
use std::thread; 
use std::result::Result; 
use std::sync::mpsc::{SyncSender, Receiver, sync_channel}; 

pub trait Element: Send + Sync { 
    fn run(&self, output: Arc<Mutex<SyncSender<i32>>>, 
        input: Arc<Mutex<Receiver<i32>>>); 
} 

pub struct TestElement {} 

impl TestElement { 
    pub fn new() -> Self { 
     TestElement {} 
    } 
} 

impl Element for TestElement { 
    fn run(&self, output: Arc<Mutex<SyncSender<i32>>>, 
        input: Arc<Mutex<Receiver<i32>>>) { 
     println!("Hello"); 
    } 
} 

pub struct Pipeline<'a> { 
    elements: Vec<Box<&'a Element>>, 
} 

impl<'a> Pipeline<'a> { 
    pub fn new(name: String) -> Self { 
     Pipeline { 
      elements: Vec::new(), 
     } 
    } 

    pub fn run(&self) -> Vec<thread::JoinHandle<()>> { 
     let mut handles = Vec::with_capacity(self.elements.len()); 

     for e in self.elements { 
      let channel = sync_channel::<i32>(1000); 
      let sender = Arc::new(Mutex::new(channel.0)).clone(); 
      let receiver = Arc::new(Mutex::new(channel.1)).clone(); 

      handles.push(thread::spawn(move || { 
       e.run(sender, receiver); 
      })); 
     } 

     handles 
    } 
} 

fn main() { 
    let mut test_element = TestElement::new(); 
    let mut pipeline = Pipeline::new("example pipeline".to_string()); 

    let handles = pipeline.run(); 
} 
+0

Können Sie Ihren Code weiter vereinfachen? Es ist immer noch ein großer Teil des Codes und es ist leichter zu beantworten und leichter zu verstehen, wenn wir nur mit einem kleinen Code arbeiten. Danke :) –

+0

Ich habe versucht, die Größe ein wenig zu reduzieren. Ich könnte mehr reduzieren, aber das würde einige der Fehler entfernen, und ich war mir nicht sicher, ob ich sollte, da ich nicht weiß, ob es sich um separate Fehler handelt oder alle auf einen Fix bezogen sind. Was denken Sie ? –

+0

Warum benutzen Sie 'Box <&'a Element>'? 'Box' bereits Heap alloziert und ist im Grunde nur ein Zeiger, also' Box 'sollte ausreichen. Dies würde auch Ihr Lebensproblem lösen (aber andere Probleme beleuchten). Beachten Sie auch, dass ein generierter Thread alles andere überdauern kann, so dass die in 'Pipeline :: run()' erzeugten Threads länger leben können als das 'Pipeline'-Objekt! Dies ist keine Antwort, sondern ein Anstoß in die richtige Richtung, hoffe ich. –

Antwort

1

Jeder note vom Compiler berichtet fügt Kontext dem vorhergehenden Fehler oder eine Warnung. Es ist kein separater Fehler; Das Beheben des Fehlers wird die Notizen verschwinden lassen.

Jetzt ist leider die Fehlermeldung nicht so klar. Das Problem ist, dass Sie versuchen, e, die vom Typ Box<&'a Element> ist, in den Abschluss zu übergeben thread::spawn, aber thread::spawn erfordert eine Schließung, die für die 'static Lebensdauer gültig ist; d.h. es enthält keine Referenzen, die kürzer sind als 'static (und 'static ist die längste verfügbare Lebensdauer: sie entspricht der Dauer des gesamten Programms). 'a ist nicht garantiert gleich 'static, daher der Fehler.

Ihr Code ist ungültig, da der Thread möglicherweise weiter ausgeführt wird, nachdem die Element, auf die sich die Referenz bezieht, freigegeben wurde.

Eine mögliche Lösung besteht darin, einfach keine Referenzen innerhalb des Vektors elements zu verwenden: Verwenden Sie Vec<Box<Element>>. Das funktioniert nur, wenn Sie die Elemente danach nicht wiederverwenden müssen.

Eine andere Option ist die Verwendung von threadsicheren Referenzzählern (Arc) anstelle einfacher Referenzen. Dies beseitigt Bedenken hinsichtlich der Lebensdauer, führt jedoch zu einem gewissen Laufzeitaufwand.

Eine letzte Option ist die Verwendung von crossbeam bis spawnscoped Gewinde. Mit dieser Option können Sie weiterhin Referenzen verwenden. Eine Sache, die Sie damit nicht tun können, ist, die Join-Handles zurückzugeben, bevor der Bereich beendet ist. Sie können die Join-Handles immer noch von Pipeline::run zurückgeben, aber crossbeam wird alle Threads verbinden, bevor scope zurückgibt.

Historische Anmerkung: Kurz vor Rust 1,0 erreicht hat, verwendet es eine Implementierung von scoped Fäden in der Standardbibliothek zu sein. Aber anstatt einen Abschluss für den Bereich zu verwenden, würde es einfach einen JoinHandle zurückgeben, und die Sicherheit dieser Implementierung stützte sich auf das Programm, das den Destruktor auf dem JoinHandle aufruft. Wenn der Destruktor nicht zur richtigen Zeit ausgeführt wurde (z. B. wenn Sie mem::forget aufgerufen haben), würde der Thread weiterhin ausgeführt werden und möglicherweise auf freigegebene Objekte verweisen. crossbeam basiert immer noch auf dem Destruktor, der aufgerufen wird, aber es ist so geschrieben, dass Benutzer der Bibliothek nie den Besitz der Scope erhalten können, so dass die Bibliothek garantieren kann, dass der Destruktor ausgeführt wird.

+0

Dank am Ende habe ich Querbalken verwendet. Die vielen Fehler haben mich verwirrt. Schande, dass sie Crossbeam in die Standardbibliothek nicht wieder einführen können. –

+0

Sie könnten sicherlich die 'Crossbeam'-Funktionalität in der Standardbibliothek einführen, aber die Entwicklung neuer Funktionen als externe Bibliothek hat mehrere Vorteile. Es gibt den Autoren mehr Flexibilität, die API in den frühen Tagen zu ändern, und es gibt ihnen mehr Publikum, da neue in der Standardbibliothek eingeführte Funktionen zuerst als _unstable_ eingeführt werden, was bedeutet, dass nur diejenigen, die einen nächtlichen Compiler verwenden, diese verwenden können. –

Verwandte Themen