2017-05-22 3 views
3

Wie erhalten Sie dieses Beispiel zu kompilieren ohne Array kopieren oder mehrere Aufrufe an b() pro Iteration - b() muss einige teure Parsing durchführen?Rust Lebenszeitproblem in Schleife

Dies ist nicht der vollständige Code, den ich geschrieben habe, aber es veranschaulicht das Problem, das ich hatte. Hier versucht Test eine Art von Streaming-Parsing-Arbeit durchzuführen. c() ist die Parsing-Funktion, die Some zurückgibt, wenn das Parsing erfolgreich war. b() ist eine Funktion, die versucht, mehr Daten aus dem Stream zu lesen, wenn c() die verfügbaren Daten noch nicht analysieren kann. Der zurückgegebene Wert ist ein Schnitt in self.v, der den analysierten Bereich enthält.

struct Test { 
    v: [u8; 10], 
    index: u8, 
} 

impl Test { 
    fn b(&mut self) { 
     self.index = 1 
    } 

    fn c(i: &[u8]) -> Option<&[u8]> { 
     Some(i) 
    } 

    fn a(&mut self) -> &[u8] { 
     loop { 
      self.b(); 

      match Test::c(&self.v) { 
       Some(r) => return r, 
       _ => continue, 
      } 
     } 
    } 
} 

fn main() { 
    let mut q = Test { 
     v: [0; 10], 
     index: 0, 
    }; 

    q.a(); 
} 

Beim Kompilieren, erzeugt es die Checker Fehler folgenden borgen:

error[E0502]: cannot borrow `*self` as mutable because `self.v` is also 
borrowed as immutable 
    --> <anon>:17:13 
    | 
17 |    self.b(); 
    |    ^^^^ mutable borrow occurs here 
18 | 
19 |    match Test::c(&self.v) { 
    |       ------ immutable borrow occurs here 
... 
24 |  } 
    |  - immutable borrow ends here 

Wenn ich a() zu ändern:

fn a(&mut self) -> Option<&[u8]> { 
    loop { 
     self.b(); 

     if let None = Test::c(&self.v) { 
      continue 
     } 

     if let Some(r) = Test::c(&self.v) { 
      return Some(r); 
     } else { 
      unreachable!(); 
     } 
    } 
} 

Dann läuft es, aber mit dem offensichtlichen Nachteile des anruf Parsing-Funktion c() zweimal.

Ich verstehe Art, dass self Wechsel während des Rückgabewert hängt davon ab, es unsicher ist, aber ich verstehe nicht, warum die unveränderliche borrow für self.v noch am Leben in der nächsten Iteration ist, wenn wir b() versuchen erneut anzurufen.

+0

sieht für mich wie ein Compiler-Fehler aus. Ich denke, die zweite Version sollte auch nicht kompilieren (und in der Tat ist es nicht, wenn Sie den Else-Zweig entfernen) –

+2

@ PaoloFalabella, es ist kein Fehler. Der Compiler kann folgern, dass 'self.b()' nicht aufgerufen wird, während das zweite Borrow von 'self.v' aktiv ist, weil die Schleife unbedingt endet, nachdem dieses Borrow genommen wurde. Wenn Sie Else Branch entfernen, ist es nicht mehr wahr. – red75prime

+0

@ red75prim ah du hast recht, danke! –

Antwort

4

Im Moment kann "Rustc nicht mit bedingten Kreditaufnahmen" handeln. Siehe hierzu comment from Gankro on issue 21906.

Es kann dem Borg keine korrekte Lebenszeit zuweisen, wenn nur ein Ausführungspfad die Schleife beendet.

kann ich diese Abhilfe vorschlagen, aber ich bin nicht sicher, dass es optimal ist:

fn c(i: &[u8]) -> Option<(usize, usize)> { 
    Some((0, i.len())) 
} 

fn a(&mut self) -> &[u8] { 
    let parse_result; 
    loop { 
     self.b(); 

     match Test::c(&self.v) { 
      Some(r) => { 
       parse_result = r; 
       break; 
      } 
      _ => {} 
     } 
    } 
    let (start, end) = parse_result; 
    &self.v[start..end] 
} 

Sie Ergebnis Parsen mit Array-Indizes konstruieren können und sie in Referenzen außerhalb der Schleife konvertieren.

Eine weitere Option ist die unsafe, um die Lebensdauer zu entkoppeln. Ich bin kein Experte in der sicheren Verwendung von unsicher, so achten Sie auf Kommentare von anderen.

fn a(&mut self) -> &[u8] { 
    loop { 
     self.b(); 

     match Test::c(&self.v) { 
      Some(r) => return unsafe{ 
       // should be safe. It decouples lifetime of 
       // &self.v and lifetime of returned value, 
       // while lifetime of returned value still 
       // cannot outlive self 
       ::std::slice::from_raw_parts(r.as_ptr(), r.len()) 
      }, 
      _ => continue, 
     } 
    } 
} 
+0

Nice graben (und Work-around)! –

+0

Das ist ein guter Workaround, aber in meinem Fall wurde der Parser mit [nom] (http://rust.unhandledexpression.com/nom) geschrieben (was immer Slices zurückgibt), so dass die Rückgabe der Länge nicht funktionieren wird. . – Dndx

+0

@Dndx, habe ich eine weitere Option zu meiner Antwort hinzugefügt. – red75prime