rust使用vec在遍历时删除元素
需求: 工作中有一个地方需要用到在遍历时把不符合条件的元素删除掉,
比如一个vec中是原始数据是
vec![1,2,3,3,4,5]
,然后我要在遍历中把等于c的元素删除掉,目的是得到vec![1,2,4,5]
第一次错误尝试
由于最开始只知道移除元素用remove方法,所以最开始是这样写的
let mut items:Vec<&str> = vec!["a", "b", "c", "c", "d", "e"];
println!("before items is {:?}", items);
for (index, item) in items.iter().enumerate() {
if *item == "c" {
items.remove(index);
}
}
println!("then items is {:?}", items);
但是报错了,报错提示为
error[E0502]: cannot borrow `items` as mutable because it is also borrowed as immutable
--> src/main.rs:8:13
|
6 | for (index, item) in items.iter().enumerate() {
| ------------------------
| |
| immutable borrow occurs here
| immutable borrow later used here
7 | if *item == "c" {
8 | items.remove(index);
| ^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
提示很明显,iter()是不可变的引用,但是调用remove的时候删除一个元素得时候,对items是可变的引用了,所以一个变量不能既是可变引用又是不可变引用,所以报错了。
第二次错误尝试
然后我进行了第二次尝试,
代码改成了,不在遍历中进行移除,把需要移除的id保存起来,在遍历结束之后,再把需要移除的元素给移除掉。
let mut items:Vec<&str> = vec!["a", "b", "c", "c", "d", "e"];
println!("before items is {:?}", items);
let mut remove_indexs: Vec<usize> = Vec::new();
for (index, item) in items.iter().enumerate() {
if *item == "c" {
remove_indexs.push(index);
}
}
println!("remove indexs is {:?}", remove_indexs);
for i in remove_indexs {
items.remove(i);
}
println!("then items is {:?}", items);
打印出的结果是
before items is ["a", "b", "c", "c", "d", "e"]
remove indexs is [2, 3]
then items is ["a", "b", "c", "e"]
不报错了,但是结果不对啊,预计是[“a”,"b","d","e"]的,怎么变成了["a", "b", "c", "e"]了。
原因是:
第二个循环需要移除索引为2和3
的两个元素。所以有两次循环:
- 第一次:在移除
索引为2
的元素之后。items变成了[“a”,"b","c","d","e"],后面的元素补上去了。这时我们的目标索引3
的index从3变成了2. - 第二次:按照代码把
索引为3
的元素移除,但是索引3的元素却成c变成了d,这段程序也就错了。
正确方法
上面方法失败之后,觉得vec应该有处理这种情况的方法,所以看了源码,找到了答案。
vec有两个方法可以实现我想要的。
vec.retain 和 vec.drain_filter
vec.retain
vec.retain 很简单,retain
的意思是保留
,所以这个方法的意思就是接收一个回调函数,然后回调函数里面返回true进行保留,返回false的就移除。
示例:
let mut vec = vec![1, 2, 3, 4];
vec.retain(|&x| x % 2 == 0);
assert_eq!(vec, [2, 4]);
所以用vec.retain来实现的话,就是这样
let mut items:Vec<&str> = vec!["a", "b", "c", "c", "d", "e"];
println!("before items is {:?}", items);
items.retain(|item| if *item == "c" {false } else { true });
println!("then items is {:?}", items);
vec.drain_filter
drain
的意思是 排出
的意思,所以这个函数就是排出过滤器,接收一个回调函数,然后把回调函数里面返回true的元素就会排出,自然也就从原本的vec里面删除掉了。然后有需要的话还可以搜集排出的元素。
示例:
let mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15];
let evens = numbers.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
let odds = numbers;
assert_eq!(evens, vec![2, 4, 6, 8, 14]);
assert_eq!(odds, vec![1, 3, 5, 9, 11, 13, 15]);
所以用vec.drain_filter来实现的话,就是这样
let mut items:Vec<&str> = vec!["a", "b", "c", "c", "d", "e"];
println!("before items is {:?}", items);
items.retain(|item| if *item == "c" {false } else { true });
let removed_items = items.drain_filter(|item| if *item == "c" { true} else { false}).collect::<Vec<_>>();
println!("then items is {:?}", items);
println!("removed item is {:?}", removed_items);
但是报错了,提示:
error[E0658]: use of unstable library feature 'drain_filter': recently added
--> src/main.rs:7:31
|
7 | let removed_items = items.drain_filter(|item| if *item == "c" { true} else { false}).collect::<Vec<_>>();
| ^^^^^^^^^^^^
|
= note: see issue #43244 <https://github.com/rust-lang/rust/issues/43244> for more information
For more information about this error, try `rustc --explain E0658`.
这个函数是属于不稳定的特性的,所以需要使用的话是有条件的
使用unstable feature的条件和步骤:
- 只有nightly才可以使用unstable
- 找到unstable feature的名字
![feature(xxx)]启用这个feature