#Help needed with nested loop and mutabillity

4 messages · Page 1 of 1 (latest)

wicked obsidian
#

I want to solve some collisions I want to do this by looping through all objects get the first one and after that loop over all objects again ( The nested loop ) en get in the second loop the second object and do the collision detection solving this is the code I got it may clarify what I mean

    fn solve_collision(&mut self) {
        let object_count = self.blobs.len();
        for i in 0..object_count {
            let first = &mut self.blobs[i];
            for j in 0..object_count {
                if i == j {
                    continue;
                };
                let second = &mut self.blobs[j];
                first.update_collision(second)
            }
        }
    }

self.blobs is just a vector with the objects
update_collision() is not written yet

sullen garnet
# wicked obsidian I want to solve some collisions I want to do this by looping through all objects...

This is actually a really neat problem because it's possible to solve it in an elegant way which shows off Rust's expressiveness. Your code as written calls x.update_collision(y) for every possible x and every possible y except where x ≡ y; this is unnecessary if update_collision() behaves symmetrically, i.e. updates both its arguments (self being the first argument).

If update_collision() is symmetric, you can call it on all possible pairs like so:

for i in 0..object_count {
    for j in (i + 1)..object_count {
        self.blobs[i].update_collision(&mut self.blobs[j]);
    }
}

This is not enough to satisfy the borrow checker, however, as self.blobs is borrowed mutably twice, once for each argument to update_collisions(). It's not enough that i and j are provably never equal, unfortunately.

It is possible to split a mutably borrowed slice into separate non-overlapping mutable references, however, either through .split_at_mut(), .split_first_mut(), .split_last_mut() or pattern matching. This lets us write this:

fn solve_collision(&mut self) {
    let mut blobs = self.blobs.as_mut_slice();
    // Alternatively:
    // while let Some((first, tail)) = blobs.split_first_mut()
    while let [first, tail @ ..] = blobs {
        // .iter_mut() is necessary here because &mut T is not Copy and Rust does not
        // automatically reborrow tail in this case. "for second in tail" calls
        // tail.into_iter() which takes self and thus moves tail, preventing us from setting
        // blobs = tail later. We could also use &mut *tail instead, which explicitly reborrows
        // before the implicit .into_iter().
        for second in tail.iter_mut() {
            first.update_collision(second);
        }
        blobs = tail;
    }
}
#

[first, ..] matches any slice (or array) with at least one element, binding the first element to first. .. matches the remaining elements as a subslice, but on its own does not bind this to a variable. tail @ .. matches the remaining elements as .. alone does, but binds this subslice to tail. You can also use @ to match patterns without destructuring, such as option @ Some(_).