2016-10-24 6 views
22

Ich schreibe einen Code in Rust, der eine Verbindung zu einem Remote-Server herstellt und je nach den von diesem Server gesendeten Nachrichten einige Statistiken berechnet oder Aktionen basierend auf diesen Statistiken ausführt. Aber das ist eher ein Lernprojekt für mich und ich bin auf ein Problem gestoßen.Bearbeiten eines Objekts aus einer Schleife, die es ausborgt

Hier ist der Code, den ich auf ein Minimum reduziert haben, das Problem zu reproduzieren:

// Repro code for error[E0502]: cannot borrow `*self` as mutable because `self.server` is also borrowed as immutable 

use std::collections::HashMap; 

struct ServerReader { 
    server: Vec<u32>, // A vec for demo purposes, but please imagine this is a server object 
    counters: HashMap<u32, usize>, 
} 

impl ServerReader { 
    fn new() -> ServerReader { 
     ServerReader { 
      server: vec!(1, 2, 5, 2, 7, 9, 1, 1, 5, 6), // Filling my "server" with some messages 
      counters: HashMap::new(), 
     } 
    } 

    fn run(&mut self) { 
     println!("Connecting..."); // ... here there should be some code to connect to the server ... 

     for message in self.server.iter() { // We wait for the network messages sent by the server, and process them as they come 
//      ----------- immutable borrow occurs here 
      println!("Received {}", message); 
      self.process_message(*message); // HOW 
//   ^^^^ mutable borrow occurs here 
     } 
//  - immutable borrow ends here 
     println!("Disconnected"); 
    } 

    fn process_message(&mut self, message: u32) { 
     // Please imagine that this function contains complex stuff 
     let counter = self.counters.entry(message).or_insert(0); 
     *counter += 1; 
    } 
} 

fn main() { 
    let mut reader = ServerReader::new(); 

    reader.run(); 

    println!("Done"); 
} 

Während ich glaube, ich verstehe, warum der Compiler unglücklich ist, ich bin zu kämpfen mit einer Lösung zu kommen. Ich kann meine Struktur außerhalb der Schleife nicht manipulieren, da ich arbeiten muss, während ich verbunden bin und dem Server zuhöre. Ich könnte auch alles direkt in die Schleife schreiben und keine Methode aufrufen, aber ich möchte nicht mit einer 1000-Zeilen-Schleife enden (und ich würde lieber verstehen, wie eine tatsächliche Lösung aussehen würde).

Antwort

16

Wie Sie heraus gearbeitet haben, können Sie keine &mut self Methode aufrufen, während Sie Teil sind Anleihen von self, also musst du irgendwie umstrukturieren.

So wie ich das tun würde, es ist, den Zustand von process_message in einen separaten Typ (in Ihrem Beispiel, das ist im Grunde die HashMap, aber in der realen Anwendung ist es wahrscheinlich mehr enthalten) benötigt aufzuspalten, und die Methode für diese Art bewegen . Dies funktioniert, weil you can separately borrow fields from a struct.

struct SomeState { 
    counters: HashMap<u32, usize>, 
} 

impl SomeState { 
    pub fn new() -> SomeState { 
     SomeState { 
      counters: HashMap::new(), 
     } 
    } 
    fn process_message(&mut self, message: u32) { 
     let counter = self.counters.entry(message).or_insert(0); 
     *counter += 1; 
    } 
} 

struct ServerReader { 
    server: Vec<u32>, 
    state: SomeState, 
} 

impl ServerReader { 
    fn new() -> ServerReader { 
     ServerReader { 
      server: vec!(1, 2, 5, 2, 7, 9, 1, 1, 5, 6), 
      state: SomeState::new(), 
     } 
    } 

    fn run(&mut self) { 
     println!("Connecting..."); 

     for message in self.server.iter() { 
      println!("Received {}", message); 
      self.state.process_message(*message); 
     } 
     println!("Disconnected"); 
    } 

} 

Eine Alternative (das kann oder in Ihrem realen Beispiel nicht möglich sein) würde Borgen in der Schleife zu vermeiden sein, es eher wie machen:

loop { 
    // if next_message() returns an owned message, ie not still borrowing 
    // self 
    let message = self.next_message(); 
    // now no borrow left 
    self.process_message(message); 
} 
+0

Interessant. Meine erste, obwohl Sie Ihren ersten Vorschlag gelesen hatte, war, dass ich den gleichen Fehler bekommen würde, weil ein mut ref auf 'self.state' zu ​​rufen process_message würde versuchen, eine mut ref von' selbst zu bekommen, aber Ihr Beispiel funktioniert in der Tat .. . Ich bin mir nicht sicher warum! Ich denke, das bedeutet, ich sollte ein wenig mehr über das Ausleihen von – Shtong

+2

lesen Sie können Felder der Struktur separat ausleihen; Das erste Dokument, das ich gefunden habe, ist https://doc.rust-lang.org/nomicon/borrow-splitting.html –

5

Vorausgesetzt, dass Sie nicht die volle ServerReader für die Verarbeitung einer Nachricht benötigen, können Sie process_message eine freie Funktion machen und einfach &mut self.counters zu ihm übergeben. Dann haben Sie getrennte Anleihen von server und counters, was in Ordnung ist.

Oder wenn Ihr nicht server Teil ServerReader größer ist, extrahieren, dass in seine eigene Struktur und process_message ein impl Methode dieser Struktur machen.

3

Um eine Änderbarkeit in einem Iterator zu ermöglichen, sollten Sie iter_mut() verwenden und an änderbaren Referenzen arbeiten (&mut message). Dann die zusätzliche borrow zu vermeiden, Sie könnten nur die Zugabe in den Körper der Schleife durchführen:

for &mut message in self.server.iter_mut() { 
    println!("Received {}", message); 
    *self.counters.entry(message).or_insert(0) += 1; 
} 
+1

Danke, aber das ist nicht wirklich das, was ich suche ich; Dies ist ein sehr vereinfachtes Beispiel, und ich bin kein großer Fan davon, alles inline zu machen (was in einer langen Schleife enden würde :) – Shtong

Verwandte Themen