2016-04-17 2 views
0

Я пишу функцию слияния для векторов тегов с подсчетами, но получаю ошибки заимствования.«использование перемещенного значения» при сопоставлении при слиянии двух векторов

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(); 
       } 
      } 
     } 
    } 
} 

выдает ошибку:

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 

и подобную ошибку для v2. Обычно это показывает местоположение проблемы и предыдущий шаг, который вызывает проблему, но не здесь.

Я пробовал много перестановок, и со следующим изменением я получил его для компиляции, но я не доволен всем клонированием и воссозданием кортежей и воссозданием Option.

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(); 
     } 
    } 
} 

Добавление, что я бы на самом деле хотел написать, для справки, в случае, если кто-то ищет вызов для заема проверки:

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() 
      } 
     } 
    } 
} 

Обратите внимание, что матч на (v1, v2) должен двигаться значения, чтобы каждый путь принудительно устанавливал v1 и v2. Все еще не так чисто, как Хаскелл, но ближе.

+0

вы имели в виду, чтобы вставить два примера вашего блока соответствия? Они выглядят одинаково для меня. –

+1

второй присваивает как v1, так и v2 на каждой ветви, но имеет чрезмерные операции с клоном –

ответ

0

Переменные v1 и v2 выходят при создании кортежа в выражении match. Вам нужно изменить эти переменные внутри match, чтобы вы не могли их одолжить.

С Option<T> вы можете использовать take() метод:

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(); 
        } 
       }; 
      } 
     }; 
    } 
} 

я переделал код последней ветви, чтобы избежать ненужных заимствований.


Недостаток этого подхода заключается в том, что вы можете забыть назначить новое значение переменной. Я бы рекомендовал, чтобы возвращать значения из выражения match:

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()) 
        } 
       } 
      } 
     }; 
    } 
} 

Удалены ненужные clone s как было упомянуто @mcarton

+1

Также нет необходимости в всех этих 'clone' благодаря итераторам утечки. – mcarton

Смежные вопросы