【问题标题】:Pop elements conditional on their content以内容为条件的流行元素
【发布时间】:2017-12-11 12:33:29
【问题描述】:

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

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 &lt; y 范围必须嵌套在 x 和 y 的范围中,那么我如何才能根据它们的第一个元素对 vec1/vec2 进行变异?

【问题讨论】:

  • 对熟悉非词法生命周期计划的人提出的问题:一旦 NLL 实现,这段代码就可以工作吗?在我看来,它似乎可以。
  • @SvenMarnach 根据我对 NLL 的了解,是的,这段代码应该可以正常工作。
  • NLL 已经登陆 Nightly,这确实适用于盒子:play.rust-lang.org/…

标签: rust borrow-checker


【解决方案1】:

你可以这样做:

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 团队会添加它。

【讨论】:

  • 如果您希望将drain_filter 添加到VecDeque,获得它的最简单方法是创建一个RFC 来提出它,然后实现它。以Vec为例,我想应该不会太复杂。
  • @MatthieuM。我对 Rust 中的流程一无所知。这对我来说不是“最简单的”。我对事情的“管理”方面比技术方面更害怕。
  • 也许最简单的词不是正确的……“最快”可能更好。另一种选择是在 IRC 上提及它,并希望有人能接受它。这是一个足够小的任务,以至于有人想在 Rust 标准库中沾沾自喜,可以合理地处理它。
  • 为了它的价值,drain_filter was added to LinkedList without an RFC(仅 2 周前!),所以我想你也不一定需要一个。
  • 我希望不必使用标志变量,但总而言之,这是一个相当干净的解决方案,嘿,它有效;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-07
  • 2015-12-24
  • 2021-12-28
  • 2011-05-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多