2016-04-17 11 views
0

Ich schreibe eine Zusammenführungsfunktion für Vektoren von Tags mit counts, aber bekomme Ausleihfehler."Verwendung von verschobenen Wert" beim Abgleich beim Verschmelzen von zwei Vektoren

fn merge(mut l1: Vec<(String, u32)>, mut l2: Vec<(String, u32)>) -> Vec<(String, u32)> { 
    let mut d1 = l1.drain(..); 
    let mut d2 = l2.drain(..); 
    let mut result = Vec::new(); 
    let mut v1 = d1.next(); 
    let mut v2 = d2.next(); 
    loop { 
     match (v1, v2) { 
      (None, None) => return result, 
      (None, Some(x)) => { 
       result.push(x.clone()); 
       v2 = d2.next() 
      } 
      (Some(x), None) => { 
       result.push(x.clone()); 
       v1 = d1.next() 
      } 
      (Some(p1), Some(p2)) => { 
       let (ref s1, t1) = p1; 
       let (ref s2, t2) = p2; 
       if s1 == s2 { 
        result.push((s1.clone(), t1 + t2)); 
        v1 = d1.next(); 
        v2 = d2.next(); 
       } else if s1 < s2 { 
        result.push(p1.clone()); 
        v1 = d1.next(); 
       } else { 
        result.push(p2.clone()); 
        v2 = d2.next(); 
       } 
      } 
     } 
    } 
} 

gibt den Fehler:

error: use of moved value: `v1` [E0382] 
     match (v1,v2) { 
       ^~ 
help: run `rustc --explain E0382` to see a detailed explanation 
note: `v1` was previously moved here because it has type `core::option::Option<(collections::string::String, u32)>`, which is non-copyable 

und einen ähnlichen Fehler für v2. Es zeigt normalerweise den Problemort und die vorherige Bewegung, die das Problem verursacht, aber nicht hier.

Ich habe viele Permutationen versucht, und mit der folgenden Änderung habe ich es kompilieren lassen, aber ich bin nicht glücklich über alle Klonen und Tupel neu erstellen und Option s neu erstellen.

match (v1, v2) { 
    (None, None) => return result, 
    (None, Some(x)) => { 
     result.push(x.clone()); 
     v1 = None; 
     v2 = d2.next(); 
    } 
    (Some(x), None) => { 
     result.push(x.clone()); 
     v1 = d1.next(); 
     v2 = None; 
    } 
    (Some(p1), Some(p2)) => { 
     let (ref s1, t1) = p1; 
     let (ref s2, t2) = p2; 
     if s1 == s2 { 
      result.push((s1.clone(), t1 + t2)); 
      v1 = d1.next(); 
      v2 = d2.next(); 
     } else if s1 < s2 { 
      result.push(p1.clone()); 
      v1 = d1.next(); 
      v2 = Some((s2.clone(), t2)); 
     } else { 
      result.push(p2.clone()); 
      v1 = Some((s1.clone(), t1)); 
      v2 = d2.next(); 
     } 
    } 
} 

Hinzufügen was ich würde wirklich zu schreiben, als Referenz, jemand, falls für eine Herausforderung für die borrow checker suchen:

fn merge(mut l1: Vec<(String, u32)>, mut l2: Vec<(String, u32)>) -> Vec<(String, u32)> { 
    let mut d1 = l1.drain(..); 
    let mut d2 = l2.drain(..); 
    let mut result = Vec::new(); 
    let mut v1 = d1.next(); 
    let mut v2 = d2.next(); 
    loop { 
     match (v1, v2) { 
      (None, None) => return result, 
      (None, Some(p2)) => { 
       result.push(p2); 
       v1 = None; 
       v2 = d2.next() 
      } 
      (Some(p1), None) => { 
       result.push(p1); 
       v1 = d1.next(); 
       v2 = None 
      } 
      (Some(p1 @ (s1, _)), o2 @ Some((s2, _))) if s1 < s2 => { 
       result.push(p1); 
       v1 = d1.next(); 
       v2 = o2 
      } 
      (o1 @ Some((s1, _)), Some(p2 @ (s2, _))) if s1 > s2 => { 
       result.push(p2); 
       v1 = o1; 
       v2 = d2.next() 
      } 
      (Some((s1, t1)), Some((_, t2))) => { 
       result.push((s1, t1 + t2)); 
       v1 = d1.next(); 
       v2 = d2.next() 
      } 
     } 
    } 
} 

Beachten Sie, dass das Spiel auf (v1, v2) bewegen sollte die Werte so, dass jeder Pfad erzwungen wird, v1 und v2 festzulegen. Immer noch nicht so sauber wie Haskell, aber näher.

+0

erwähnt Meinen Sie zwei verschiedene Beispiele für Ihr Spiel Block einfügen? Sie sehen gleich aus. –

+1

der zweite wird sowohl v1 als auch v2 auf jedem Zweig zugewiesen, hat aber exzessive Klonoperationen –

Antwort

0

Die Variablen v1 und v2 werden beim Erstellen eines Tupels im Ausdruck match verschoben. Sie müssen diese Variablen innerhalb der match ändern, damit Sie sie nicht ausleihen können.

Mit Option<T> Sie take() Methode verwenden:

fn merge(mut l1: Vec<(String, u32)>, mut l2: Vec<(String, u32)>) -> Vec<(String, u32)> { 
    let mut d1 = l1.drain(..); 
    let mut d2 = l2.drain(..); 
    let mut result = Vec::new(); 
    let mut v1 = d1.next(); 
    let mut v2 = d2.next(); 
    loop { 
     match (v1.take(), v2.take()) {//Takes the value out of the option, leaving a None in its place. 
      (None, None) => return result, 
      (None, Some(x)) => { 
       result.push(x); 
       v2 = d2.next() 
      }//v1 is None 
      (Some(x), None) => { 
       result.push(x); 
       v1 = d1.next() 
      }//v2 is None 
      (Some(p1), Some(p2)) => { 
       use std::cmp::Ordering::{Equal, Less, Greater}; 
       match p1.0.cmp(&p2.0) { 
        Equal => { 
         result.push((p1.0, p1.1 + p2.1)); 
         v1 = d1.next(); 
         v2 = d2.next(); 
        } 
        Less => { 
         result.push(p1); 
         v1 = d1.next(); 
         v2 = Some(p2); 
        }//restore v2 
        Greater => { 
         result.push(p2); 
         v1 = Some(p1); //restore v1 
         v2 = d2.next(); 
        } 
       }; 
      } 
     }; 
    } 
} 

ich den Code des letzten Zweig verändert haben, um unnötige Kreditaufnahme zu vermeiden.


Nachteil dieses Ansatzes ist, dass Sie vergessen können, einer Variablen einen neuen Wert zuzuweisen. Ich würde empfehlen, die Werte aus dem match Ausdruck zurückzukehren:

fn merge(mut l1: Vec<(String, u32)>, mut l2: Vec<(String, u32)>) -> Vec<(String, u32)> { 
    let mut d1 = l1.drain(..); 
    let mut d2 = l2.drain(..); 
    let mut result = Vec::new(); 
    let mut v = (d1.next(), d2.next()); 
    loop { 
     v = match (v.0.take(), v.1.take()) { 
      (None, None) => return result, 
      (None, Some(x)) => { 
       result.push(x); 
       (None, d2.next()) 
      } 
      (Some(x), None) => { 
       result.push(x); 
       (d1.next(), None) 
      } 
      (Some(p1), Some(p2)) => { 
       use std::cmp::Ordering::{Equal, Less, Greater}; 
       match p1.0.cmp(&p2.0) { 
        Equal => { 
         result.push((p1.0, p1.1 + p2.1)); 
         (d1.next(), d2.next()) 
        } 
        Less => { 
         result.push(p1); 
         (d1.next(), Some(p2)) 
        } 
        Greater => { 
         result.push(p2); 
         (Some(p1), d2.next()) 
        } 
       } 
      } 
     }; 
    } 
} 

unnötig entfernt clone s, wie durch @mcarton

+1

Es gibt auch keine Notwendigkeit für all diese 'Klon's dank der 'drain'ed Iteratoren. – mcarton