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
感興趣的可以參考:https://blog.csdn.net/varding/article/details/48206689 來啓用
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://www.cnblogs.com/ipdaxia/p/15933029.html