"use of moved value" 合并两个向量时进行匹配

"use of moved value" when matching while merging two vectors

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

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 的类似错误。它通常显示问题位置和导致问题的前一步,但这里没有。

我尝试了很多排列组合,通过以下更改我可以编译它,但我对所有克隆和重新创建元组以及重新创建 Options 感到不满意。

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 干净,但更接近。

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

为了避免不必要的借用,我修改了最后一个分支的代码。


这种方法的缺点是您可能会忘记为变量分配新值。我建议 return 来自 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

提到的不必要的clones