How to use a vector value to test a condition and push to it if the condition is true

Hey everyone,

I want to have a simple code to find all primes smaller than 100. This is just the beginning so don't mind the correctness of the actual results and it is just for me to better learn Rust. So far I have:

fn main() {
    
    let mut primes: Vec<i32> = vec![2, 3, 5, 7, 11, 13, 17, 19];
    
    for i in 1..=100 as i32 {
        for p in primes.iter_mut() {
            let r = i % p;
            if r != 0 {
               primes.push(i);
            }
        }
    }
}

However, I am not able to figure out how to how to make the code compile. It gives me this error:

cannot mod `i32` by `&mut i32`the trait `Rem<&mut i32>` is not implemented for `i32`the following other types implement trait rustc (E0277)

If I change the line where the remainder is calculated to:

let r = i % p.owned();

It gives me an error:

cannot borrow `primes` as mutable more than once at a timesecond mutable borrow occurs here rustc (E0499)

Any ideas how I could solve this issue? Any tips on how to make the code more rusty is also appreciated!

You cannot push to a collection while iterating over it.

You can use a simple indexed loop:

        let primes_len = primes.len(); // Save the len before pushing the new prime.
        for p in 0..primes_len {
            let r = i % primes[p];
            if r != 0 {
               primes.push(i);
            }
        }

I knew there was a simple solution to this, but I didn't think it would be that simple... Thank you for simple and quick answer!

The short explanation is that an iterator is a pointer into the data allocated for the vector, and pushing to the vector might need to reallocate and thus move the data out from under the iterator.

Using an index instead of an iterator means that when the data moves, the index is still valid for the new location. Be aware that since you're increasing the length after getting it, you won't iterate over the items pushed this time through the outer loop. You would have to do something like iterate "forever" with for j in 0.. {} (note the missing upper bound) and break inside the loop if you wanted to do that.

2 Likes

On a first glance, it looks to me like your logic for testing for primes is off and - if corrected - you wouldn't need to modify the Vec while iterating it, anyways.

4 Likes

Although it is not the focus of the question I want to mention that your algorithm is incorrect. You want something like this, which -- as stated by @steffahn -- will eliminate the need to modify the primes vector inside the primes loop:

fn main() {
    let mut primes: Vec<i32> = vec![2, 3, 5, 7, 11, 13, 17, 19];

    'outer: for i in 2..=100 {
        for p in primes.iter() {
            let r = i % p;
            if r == 0 {
                continue 'outer;
            }
        }
        primes.push(i);
    }

    println!("{primes:?}");
}

See rustexplorer for demo.

1 Like

As a next step, it might be a nice exercise to re-write the inner loop using .any() or .all() ^^

5 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.