rust - Mutating an item inside of nested loops -
i'm trying write program evenly (or close possible) distribute points surface of sphere. i'm trying achieve placing n points randomly unit sphere, , running multiple steps points repel each other.
the problem in loop on points array. code below loops on each point, , loop inside that, again loops on each point , calculates repulsive force between each point pair.
for point in points.iter_mut() { point.movement = quaternion::identity(); neighbour in &points { if neighbour.id == point.id { continue; } let angle = point.pos.angle(&neighbour.pos); let axis = point.pos.cross(&neighbour.pos); let force = -(1.0/(angle*angle)) * update_amt; point.movement = point.movement * quaternion::angle_axis(angle, axis); } } i'm getting error:
src/main.rs:71:27: 71:33 error: cannot borrow `points` immutable because borrowed mutable src/main.rs:71 neighbour in &points { and explanation
the mutable borrow prevents subsequent moves, borrows, or modification of `points` until borrow ends i'm new rust, coming c++ background, , i've got no idea how make kind of pattern work in rust.
any solutions appreciated, i'm absolutely stuck ideas. thanks.
edit:
i've worked out solution, have detailed in answer below. still don't know if it's best way around problem, i'm still open suggestions.
there's few ways 1 can write this.
one suggestion, of separating movements separate vector, since ensures mutable borrow of movement value doesn't force compiler conservatively disallow access rest of points avoid mutable borrows aliasing. in rust, &mut reference can never alias: if values accessible 1 path, guaranteed any/all mutations memory safe, if there aliasing takes more effort (including runtime checks) ensure mutations safe.
another use indicies outer loop:
for in 0..points.len() { let mut movement = quaternion::identity(); neighbour in &points { if neighbour.id == points[i].id { continue } // ... movement = movement * quaternion::angle_axis(angle, axis); } points[i].movement = movement } a third method change how loops work: when considering interaction between point , neighbour, update both point , neighbour @ same time. allows 1 iterate "triangularly": point 0 interacts 1..n, point 1 interacts 2..n, ... (where n = points.len()). can done in way compiler understands won't alias.
first 1 must reset movements 1. main loops consist of outer loop selects single element, , 1 can "reborrow" iterator inner loop. outer loop cannot use for, since for take ownership of iterator, fortunately, though, while let allows 1 write neatly:
for point in &mut points { point.movement = quaternion::identity() } let mut iter = points.iter_mut(); while let some(point) = iter.next() { // reborrows iterator converting slice // (with shorter lifetime) coerced iterator // `for`. iterates on elements after `point` neighbour in &mut iter[..] { // `neighbour` automatically distinct `point` let angle = point.pos.angle(&neighbour.pos); let axis = point.pos.cross(&neighbour.pos); let force = -(1.0 / (angle*angle)) * update_amt; point.movement = point.movement * quaternion::angle_axis(angle, axis); neighbour.movement = neighbour.movement * quaternion::angle_axis(angle, -axis); } } nb. believe axis must reversed neighbour update. (i haven't compiled this, it's close.)
theoretically, latter offers approximately 2× speed-up vs. of other suggestions far. @ least, reduces number of calculations of angle, axis & force n * (n - 1) n * (n - 1) / 2.
Comments
Post a Comment