"使用移动值"在合并两个向量时进行匹配

时间:2016-04-17 03:05:48

标签: rust borrowing

我正在为带有计数的标签向量编写合并函数,但我正在借用错误。

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 s感到不满意

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)上的匹配应移动值,以便强制每个路径设置v1v2。仍然不如Haskell那么干净,但更接近。

1 个答案:

答案 0 :(得分:0)

v1表达式中创建元组时,变量v2match会移出。您需要在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())
                    }
                }
            }
        };
    }
}

删除了@mcarton

提到的不必要的clone