How to iterate over the same vector 2x with mut?

I am trying to go thru the vector 2x to calc distances between all ships in the simulation. Clippy told me to iterate via for i in &ships and not to use i in 0..ships.len(). So now I have this error and it makes sense but I am at a loss on how to fix it. As I understand only 1 iterator needs to be mut, the other just reads values.

Code in question is here:

    // now calculate distance to shortest target
    for i in &mut ships {
        for j in &ships {
            if i.name != j.name {
                let d = i.location.calc_distance(j.location);
                if d < i.s_target {
                    i.s_target = d;
                }
                println!("{} targets {} = {}", 
                    i.name, j.name, d);
            }
        }
    }

You'll have to clone ships. iter_mut takes a mutable(unique) reference to self.

This may be a false positive from Clippy, since it saw one loop could be replaced but couldn't tell the other loop was also using the same variable. The indexing method works.

The other thing you can do is use Cell. This is easiest if your type implements Copy.

let ship_cells = Cell::from_mut(&mut ships).as_slice_of_cells();
for ic in ship_cells {
    for jc in ship_cells {
        let i = ic.get();
        let j = jc.get();
        if i.name != j.name {
            let d = i.location.calc_distance(j.location);
            if d < i.s_target {
                i.s_target = d;
            }
            println!("{} targets {} = {}", 
                i.name, j.name, d);
        }
        ic.set(i);
        // jc.set(j);
    }
}

You may also consider improving your algorithm if the order of the prints doesn't matter:

for split in 0..ships.len() - 1 {
    let end = ships[split..];
    let (i, rest) = end.split_first_mut().unwrap();
    for j in rest {
        let d = i.location.calc_distance(j.location);
        if d < i.s_target {
            i.s_target = d;
        }
        println!("{} targets {} = {}", i.name, j.name, d);

        let d = j.location.calc_distance(i.location);
        if d < j.s_target {
            j.s_target = d;
        }
        println!("{} targets {} = {}", j.name, i.name, d);
    }
}
3 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.