如何对可变借用的向量进行分区?

3

如果我有一个本地的、可变的向量,我可以按照以下方式将其分区,这里是从 partition 文档中复制的。

let mut a = vec![1, 2, 3, 4];
let (even, odd): (Vec<i32>, Vec<i32>) = a.into_iter().partition(|&n| n % 2 == 0);

a向量在此过程中被消耗(移动)。然而,如果我有一个借用的可变引用,则同一调用分区的代码不起作用。如果我尝试在这种情况下使用相同的代码,则会出现错误:

error[E0277]: the trait bound `std::vec::Vec<i32>: std::iter::Extend<&mut i32>` is not satisfied
 --> src/main.rs:2:59
  |
2 |     let (even, odd): (Vec<i32>, Vec<i32>) = a.into_iter().partition(|n| **n % 2 == 0);
  |                                                           ^^^^^^^^^ the trait `std::iter::Extend<&mut i32>` is not implemented for `std::vec::Vec<i32>`
  |
  = help: the following implementations were found:
            <std::vec::Vec<T> as std::iter::Extend<&'a T>>
            <std::vec::Vec<T> as std::iter::Extend<T>>

基于如何将Rust可变引用变为不可变引用?,我写了以下代码,它能够编译并将正确的值放入evenodd中。

fn doit(a: &mut Vec<i32>) {
    let (even, odd): (Vec<i32>, Vec<i32>) = (&*a).into_iter().partition(|&n| n % 2 == 0);
    println!("even {:?}, odd {:?}, a {:?}", even, odd, a);
}

然而,尽管我使用了 into_iter(),这并没有消耗原始向量。在这里,涉及到可变性、借用或迭代器等一些我还不太理解的概念。


这不会消耗原始向量 - 您不能消耗您不拥有的东西,那只是基本的 Rust。为什么您 想要 这样做呢? - Shepmaster
@Shepmaster,我真正想要的是retain()返回一个已删除元素的向量,但partition()是我找到的最接近的东西,所以我想在调用partition()后重新填充可变向量。可能我使用了错误的工具,我应该只是使用for循环迭代并使用swap_remove_back。(我的实际代码正在使用VecDeque) - Mr. Kevin
你认为这是 Is there a way to drain parts of a vector based on a predicate? 的重复问题吗?在我看来是这样的。 - Shepmaster
很接近了,但这个问题更多地涉及到into_iter与借用的交互方式,而那里的两个答案都没有@Jmb在下面提供的解决方法。 - Mr. Kevin
“and neither of the answers” — 因为它已经在问题中了。 - Shepmaster
2个回答

4

正如其他人所暗示的那样,你不能移动原始向量,因为你没有拥有它。但是你可以将向量中的所有值移出,使其为空。这可以通过使用drain方法来实现:

fn doit(a: &mut Vec<i32>) {
    let (even, odd): (Vec<i32>, Vec<i32>) = a.drain(..).partition(|&n| n % 2 == 0);
    println!("even {:?}, odd {:?}, a {:?}", even, odd, a);
}

playground


1
原因在于,对于&'a mut Vec<T>&'a Vec<T>,IntoIterator trait的实现方式与Vec<T>不同。前两者通过值创建迭代器,而Vec<T>版本创建消耗迭代器,即从向量中移动每个值(从开始到结束)。调用into_iter()后无法再使用该向量。因此,您可以认为前两个&'a mut Vec<T>&'a Vec<T>是使用引用内部的值来创建迭代器。而Vec<T>则可以将其视为将值从Vec<T>中移除并将其放入迭代器中。Shepmaster的评论指出,不能消耗您不拥有的东西是它们实现不同的原因。

还要注意,有多种方法可以获取迭代器。 根据Rust文档:

  • iter()用于迭代&T
  • iter_mut()用于迭代&mut T
  • into_iter()用于迭代T

因此,您可以使用iter()而不是into_iter(),这样还可以保持函数不变而不需要使用(&*a)

fn doit(a: &mut Vec<i32>) {
    let (even, odd): (Vec<i32>, Vec<i32>) = a.iter().partition(|&n| n % 2 == 0);
    println!("even {:?}, odd {:?}, a {:?}", even, odd, a);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接