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的兩個元素。所以有兩次循環:

正確方法

上面方法失敗之後,覺得 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 的條件和步驟:

  1. 只有 nightly 纔可以使用 unstable
  2. 找到 unstable feature 的名字
  3. ![feature(xxx)] 啓用這個 feature

感興趣的可以參考:https://blog.csdn.net/varding/article/details/48206689 來啓用

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://www.cnblogs.com/ipdaxia/p/15933029.html