How to delete element when iterating a vec?

For example the following codes will throw exception:index out of bounds: the len is 3 but the index is 3

   let mut vecc = vec![1, 2, 3, 4];

    for i in 0..vecc.len() {
        if vecc[i] == 1 {
            vecc.remove(i);
        }
    }

Mutating while iterating is a tough sell in many languages, Rust among them. It's doable, but I would try to find a way not to. There's often a method you can call to avoid an explicit loop, such as retain for your example:

vecc.retain(|&i| i != 1);

Playground

5 Likes

The following can also work:

    let mut vecc = vec![1, 2, 3, 4];
    let mut idx = 0 as usize;

    while  idx < vecc.len() {
        if vecc[idx] == 1 {
            vecc.remove(idx);
            continue;
        }
        idx = idx + 1;
    }
1 Like

It is great!

Note that calling remove in a loop is also more inefficient. Vec::remove takes linear1 time (it has to shift all the elements to the right of the removed element one to the left in memory); thus calling .remove in a loop can take quadratic1 time; whereas .retain only takes linear1, 2 time for the whole process (it can use an implementation that – as an intermediate state – leaves some “slots” of memory empty during the iteration, with the effect of only having to move each item of the vec at most once).

1 linear/quadratic with respect to the length of the vector
2 assuming that the closure given to .retain only needs constant time

5 Likes

To translate this into code, Vec::retain() works similarly¹ to this:

    let mut vecc = vec![1, 2, 3, 4];

    let mut idx_wr = 0usize;
    for idx_rd in 0..vecc.len() {
        if ! (vecc[idx_rd] == 1) {
            vecc.swap(idx_wr, idx_rd);
            idx_wr += 1;
        }
    }
    
    vecc.truncate(idx_wr);

¹ I haven't looked at the actual code for retain; it probably uses unsafe to perform a move instead of the swap call I used.

2 Likes

yup, that’s exactly what it does.