2016-05-02 15 views
3

Ich habe eine struct, die wandelbar Referenzen hält Objekte Merkmal:Iterieren über einen Vektor von wandelbaren Verweise auf Charakterzug Objekte

trait Task { 
    fn do_it(&mut self); 
} 

struct Worker<'a> { 
    tasks: Vec<&'a mut Task>, 
} 

Bei einem Verfahren zur Worker, die ich über die Aufgaben wiederholen wollen, und rufen ihre do_it:

impl<'a> Worker<'a> { 
    pub fn work(&mut self) { 
     for task in self.tasks.iter() { 
      self.work_one(*task); 
     } 
    } 

    fn work_one(&self, task: &mut Task) { 
     task.do_it(); 
    } 
} 

Leider die borrow checker mich nicht es tun lassen:

error[E0389]: cannot borrow data mutably in a `&` reference 
    --> src/main.rs:12:27 
    | 
12 |    self.work_one(*task); 
    |       ^^^^^ assignment into an immutable reference 

Ich kann Worker nicht generisch machen, weil ich will, dass es Aufgaben vieler Arten hält. Ich brauche auch Aufgaben, um veränderbar zu sein. Wie mache ich das in Rust?

Antwort

7

Sie rufen tasks.iter() die unveränderlichen Verweise auf die Elemente von Vec erzeugt. Sie erhalten tatsächlich &&mut Task, eine unveränderliche Referenz zu einem veränderlichen (deshalb beschwert sich der Rust-Compiler).

Um dies zu lösen, rufen Sie tasks.iter_mut() auf, um einen Iterator für änderbare Referenzen zu erhalten.

Das zweite Problem ist das Aufrufen der Definition work_one als eine Methode. Sie leihen sich beim Iterieren bereits eine veränderbare Referenz von self aus, damit Sie keinen weiteren Kredit bekommen.

Arbeitsbeispiel (playground):

trait Task { 
    fn do_it(&mut self); 
} 

struct Worker<'a> { 
    tasks: Vec<&'a mut Task>, 
} 

impl<'a> Worker<'a> { 
    pub fn work(&mut self) { 
     for task in self.tasks.iter_mut() { 
      Worker::work_one(*task); 
     } 
    } 

    fn work_one(task: &mut Task) { 
     task.do_it(); 
    } 
} 

noch den Zugriff auf self in work_one haben diese Abhilfe verwendet werden kann. Im Prinzip werden nur die beiden Vektoren getauscht, so dass Sie beim Iterieren nicht wirklich self ausleihen und dann zurück tauschen. Das ist hässlich, vielleicht gibt es hier ein besseres Muster, vielleicht schlägt jemand anderes etwas Besseres vor.

pub fn work(&mut self) { 
    let mut tasks = vec![]; 
    mem::swap(&mut tasks, &mut self.tasks); 
    for task in tasks.iter_mut() { 
     self.work_one(*task); 
    } 
    mem::swap(&mut tasks, &mut self.tasks); 
} 

Eine schönere Alternative von @Veedrac vorgeschlagen:

fn work(&mut self) { 
    let mut tasks = mem::replace(&mut self.tasks, Vec::new()); 
    for task in &mut tasks { 
     self.work_one(*task); 
    } 
    self.tasks = tasks; 
} 
+0

"unveränderlicher Bezug auf einen veränderbaren einen" Aaah, das ist ein Stück, das ich fehlte. Und bezüglich der Bezugnahme auf das Selbst: Dies bedeutet, dass wenn ich irgendeinen Zustandswechsel vornehmen müsste, ich es mit dem Körper der "for" -Schleife machen müsste. Bekomme ich es richtig? – Tomo

+0

@Tomo Aktualisiert die Antwort, nicht wirklich eine ideale Lösung. Hoffe jemand anders kann etwas besseres vorschlagen. – Arjan

+0

Ja, das sieht hässlich aus. :) – Tomo

1

Sie müssen einen veränderlichen Verweis auf jedes Element haben. iter gibt unveränderliche Referenzen zurück. Und ein unveränderlicher Verweis auf eine veränderliche Variable ist nicht selbst veränderlich. Verwenden Sie stattdessen iter_mut oder for task in &mut self.tasks.

Dann wird die einfachste, was zu tun work_one in `Arbeit Inline:

pub fn work(&mut self) { 
    for task in self.tasks.iter_mut() { 
     task.do_it() 
    } 
} 

Leider Spaltung dies in zwei Funktionen sehr schmerzhaft ist. Sie müssen garantieren, dass der Aufruf self.work_one wird self.tasks nicht ändern. Rust verfolgt diese Dinge nicht über Funktionsgrenzen hinweg. Daher müssen Sie alle anderen Elementvariablen aufteilen und sie separat an eine Funktion übergeben. auch

Siehe:

Verwandte Themen