流行元素以其内容为条件

时间:2017-12-11 12:33:29

标签: rust borrow-checker

借用检查器让我很难找到优雅的实现方式。这是一个最小的例子:

use std::collections::VecDeque;

fn main() {
    let mut vec1 = VecDeque::new();
    vec1.push_back(2.);

    let mut vec2 = VecDeque::new();
    vec2.push_back(1.);

    while let (Some(x), Some(y)) = (vec1.front_mut(), vec2.front_mut()) {
        if x < y {
            *y -= *x;
            vec1.pop_front();
        } else {
            *x -= *y;
            vec2.pop_front();
        }
    }

    assert_eq!(vec2.len(), 0);
    assert_eq!(vec1.pop_front(), Some(1.));
}

这不会编译:

error[E0499]: cannot borrow `vec1` as mutable more than once at a time
  --> src/main.rs:13:13
   |
10 |     while let (Some(x), Some(y)) = (vec1.front_mut(), vec2.front_mut()) {
   |                                     ---- first mutable borrow occurs here
...
13 |             vec1.pop_front();
   |             ^^^^ second mutable borrow occurs here
...
18 |     }
   |     - first borrow ends here

error[E0499]: cannot borrow `vec2` as mutable more than once at a time
  --> src/main.rs:16:13
   |
10 |     while let (Some(x), Some(y)) = (vec1.front_mut(), vec2.front_mut()) {
   |                                                       ---- first mutable borrow occurs here
...
16 |             vec2.pop_front();
   |             ^^^^ second mutable borrow occurs here
17 |         }
18 |     }
   |     - first borrow ends here

if x < y范围必然嵌套在xy的范围内,那么我怎样才能以{1}} / vec1为条件进行变更元件?

1 个答案:

答案 0 :(得分:3)

你可以这样做:

use std::collections::VecDeque;

fn main() {
    let mut vec1 = VecDeque::new();
    vec1.push_back(2.);

    let mut vec2 = VecDeque::new();
    vec2.push_back(1.);

    loop {
        let pop_vec1;

        if let (Some(x), Some(y)) = (vec1.front_mut(), vec2.front_mut()) {
            if x < y {
                *y -= *x;
                pop_vec1 = true;
            } else {
                *x -= *y;
                pop_vec1 = false;
            }
        } else {
            break;
        }

        if pop_vec1 {
            vec1.pop_front();
        } else {
            vec2.pop_front();
        }
    }

    assert_eq!(vec2.len(), 0);
    assert_eq!(vec1.pop_front(), Some(1.));
}

或者如果您更喜欢使用较少行的循环:

loop {
    let pop_vec1 = match (vec1.front_mut(), vec2.front_mut()) {
        (Some(ref x), Some(ref mut y)) if x < y => { **y -= **x; true },
        (Some(x), Some(y)) => { *x -= *y; false },
        _ => break,
    };

    VecDeque::pop_front(if pop_vec1 { &mut vec1 } else { &mut vec2 });
}

不是很优雅,但它有效。

VecDeque缺少方法like Vec::drain_filter。也许Rust团队会添加它。