How do I mutate a structure I am looping over? - loops

This question is motivated by this CodinGame puzzle.
I am implementing a basic pathfinding algorithm using Dijkstra's method. It uses a boundary HashMap and a finished HashMap to hold pathfinding-related node info. In a particular loop, I find the highest-valued node in boundary, remove the node, add the node to finished, and add/update the node's neighbors' info in boundary.
Attempting to mutate boundary while looping over it is making Rust's borrow checker queasy, but the logic of the loop seems sound to me. How do I rewrite it so that the compiler shares my confidence? (Or fix the errors I'm missing, if that's the issue.)
Code:
On Rust Playground here
use std::io;
use std::collections::{HashSet, HashMap};
use std::cmp::Ordering;
use std::cell::RefCell;
struct NodeInfo {
nbrs: HashSet<i32>,
gwlinks: i32,
}
#[derive(PartialEq,PartialOrd)]
struct PFInfo {
avg: f32,
cum: i32,
dist: i32,
prev: Option<i32>,
}
impl Eq for PFInfo {}
impl Ord for PFInfo {
fn cmp(&self, other: &PFInfo) -> Ordering {
match self.partial_cmp(other) {
Some(ord) => ord,
None => Ordering::Equal
}
}
}
type Graph = HashMap<i32, RefCell<NodeInfo>>;
type PFGraph = HashMap<i32, PFInfo>;
// Find the path that passes the most gateway links per distance traveled,
// starting at a given node. This is meant to simulate the behavior of an
// "agent" which traverses the graph in the puzzle mentioned above.
fn generate_path(si: &i32, graph: &Graph) -> Vec<i32> {
let n = graph.len();
let mut boundary = PFGraph::with_capacity(n);
let mut finished = PFGraph::with_capacity(n);
boundary.insert( si.clone(),
PFInfo {
avg: 0.,
cum: graph.get(&si).unwrap().borrow().gwlinks,
dist: 0,
prev: None } );
// Keep grabbing the key corresponding the highest value until `boundary` is
// empty
while let Some( (currid, _) ) = boundary.iter().max_by_key(|x| x.1) {
// Move the node from `boundary` to `finished`
let val = boundary.remove(&currid).unwrap();
finished.insert(currid.clone(), val);
// Add or update all adjacent nodes that are not in `finished`
for nbrid in graph.get(&currid).unwrap()
.borrow()
.nbrs.iter()
.filter(|x| !finished.contains_key(x)) {
let currval = finished.get(&currid).unwrap();
let prev = Some(currid.clone());
let dist = currval.dist + 1;
let cum = currval.cum + graph.get(nbrid).unwrap().borrow().gwlinks;
let avg = cum as f32 / dist as f32;
boundary.insert(
nbrid.clone(),
PFInfo {
avg: avg,
cum: cum,
dist: dist,
prev: prev,
}
);
}
}
let mut path = Vec::new();
let mut currid = finished.iter().max_by_key(|x| x.1).unwrap().0.clone();
path.push(currid.clone());
while let Some(previd) = finished.get(&currid).unwrap().prev {
path.push(previd.clone());
currid = previd.clone();
}
path.reverse();
path
}
macro_rules! parse_input {
($x:expr, $t:ident) => ($x.trim().parse::<$t>().unwrap())
}
#[test]
fn test_generate_path() {
let mut inputs = "8 13 2
6 2
7 3
6 3
5 3
3 4
7 1
2 0
0 1
0 3
1 3
2 3
7 4
6 5
4
5".lines();
let header = inputs.next().unwrap().split_whitespace().collect::<Vec<_>>();
let n = parse_input!(header[0], i32); // the total number of nodes in the level, including the gateways
let l = parse_input!(header[1], i32); // the number of links
let e = parse_input!(header[2], i32); // the number of exit gateways
let mut graph = Graph::with_capacity(n as usize);
for node in 0..n {
graph.insert(node, RefCell::new(NodeInfo{ nbrs: HashSet::new(), gwlinks: 0 }));
}
let graph = graph;
for _ in 0..l as usize {
let link = inputs.next().unwrap();
let nodes = link.split(" ").collect::<Vec<_>>();
let n1 = parse_input!(nodes[0], i32); // N1 and N2 defines a link between these nodes
let n2 = parse_input!(nodes[1], i32);
graph.get(&n1).unwrap().borrow_mut().nbrs.insert(n2);
graph.get(&n2).unwrap().borrow_mut().nbrs.insert(n1);
}
let mut gateways = HashSet::new();
for _ in 0..e as usize {
let ei = parse_input!(inputs.next().unwrap(), i32); // the index of a gateway node
gateways.insert(ei);
}
let gateways = gateways;
for gwid in &gateways {
for gwnbr in &graph.get(gwid).unwrap().borrow().nbrs {
(&graph).get(&gwnbr).unwrap().borrow_mut().gwlinks += 1;
}
}
assert_eq!(generate_path(&0, &graph), vec![0, 3]);
}
Errors:
rustc 1.18.0 (03fc9d622 2017-06-06)
error[E0502]: cannot borrow `boundary` as mutable because it is also borrowed as immutable
--> <anon>:53:19
|
50 | while let Some( (currid, _) ) = boundary.iter().max_by_key(|x| x.1) {
| -------- immutable borrow occurs here
...
53 | let val = boundary.remove(&currid).unwrap();
| ^^^^^^^^ mutable borrow occurs here
...
76 | }
| - immutable borrow ends here
error[E0502]: cannot borrow `boundary` as mutable because it is also borrowed as immutable
--> <anon>:66:13
|
50 | while let Some( (currid, _) ) = boundary.iter().max_by_key(|x| x.1) {
| -------- immutable borrow occurs here
...
66 | boundary.insert(
| ^^^^^^^^ mutable borrow occurs here
...
76 | }
| - immutable borrow ends here
error: aborting due to 2 previous errors

I found a solution to my issue, and it's somewhat generalizable, which is what I was hoping for. The problem was that an implicit reference created in the while let statement was living to the end of the loop even though it was only needed on that one line. The borrow begins at .iter() and is no longer needed once the referenced value is cloned at the end of the expression.
while let Some( (currid, _) ) = boundary.iter().max_by_key(|x| x.1).clone() {
// ^---where borrow begins ^---where borrow could end
// Move the node from `boundary` to `finished`
let val = boundary.remove(&currid).unwrap();
finished.insert(currid.clone(), val);
...
} // <--- where borrow does end
The trick was moving the binding of currid into the loop. When the value is borrowed in the while let statement, the borrow checker apparently thinks the borrow needs to last throughout the loop. If, instead, the implicit borrow is made in a regular let binding, the borrow checker is smart enough to realize the borrow can be safely discarded at the end of the line.
while !boundary.is_empty() {
let currid = boundary.iter().max_by_key(|x| x.1).unwrap().0.clone();
// ^---where borrow begins ^---where borrow ends
// Move the node from `boundary` to `finished`
let val = boundary.remove(&currid).unwrap();
finished.insert(currid.clone(), val);
...
}
I guess the take away here is that if you need to mutate a structure in a loop that depends on it, put any borrows of the structure inside the loop and keep those borrows as short as possible – for example, by using clone.
This might be one of the situations eventually mitigated by the proposed non-lexical lifetimes.

Related

How to create a mutable Cycle iterator over multiple arrays

My family and I had fun playing Mancala together and I really liked the simplicity of its rules but have a few questions such as "What is the highest possible score". I thought it would be a fun little project to implement in Rust but I am stuck and need help.
There are many rules of how to play Mancala. I want to implement this version: https://www.youtube.com/watch?v=-A-djjimCcM. Knowing the rules of the game makes understanding my problem easier, but it probably isn't required to get it.
This is how a Mancala board looks like:
| |04|04|04|04|04|04| |
|00|-----------------|00|
| |04|04|04|04|04|04| |
Each of the numbers represent a hole. The numbers on the left and right in the bigger boxes represent a "mancala". A mancala is basically a hole where you count your points. The one on your right, is your own mancala, the one on the left is your opponent's mancala. The numbers represent the number of marbles in that specific hole.
In the game, you can select a hole, take all its marbles and then drop one marble in each of the next holes/mancala until you run out of marbles. You skip your opponent's mancala. This is what I am struggling with.
This is how I tried to solve it:
The Mancala board is a struct that has four arrays storing Holes. One for each of the holes on the side of the player and one for their mancala. I want to chain together and cycle through three of these arrays of Holes so I can run an associated function on those Holes (the opponent's mancala gets skipped). This is my code:
pub const STARTING_MARBLES: i8 = 4;
pub const NO_OF_HOLES_OF_EACH_PLAYER: usize = 6;
// There can be two players
#[derive(Debug, Copy, Clone)]
pub enum Player {
A,
B,
}
// A dip in a mancala board that can contain a number of marbles
#[derive(Debug, Copy, Clone)]
struct Hole {
marbles: i8,
}
impl Hole {
// Adds x marbles to the hole
fn add_x(&mut self, x: i8) {
self.marbles += x;
}
// Takes all marbles from the hole and returns their number
fn take_all(&mut self) -> i8 {
let marbles = self.marbles;
self.marbles = 0;
marbles
}
// Returns the number of marbles in the hole
fn count(&self) -> i8 {
self.marbles
}
}
// A mancala board with all its holes and mancalas to count the players points
#[derive(Debug, Copy, Clone)]
pub struct Board {
holes_a: [Hole; NO_OF_HOLES_OF_EACH_PLAYER],
holes_b: [Hole; NO_OF_HOLES_OF_EACH_PLAYER],
mancala_a: [Hole; 1],
mancala_b: [Hole; 1],
}
impl Board {
// Create, initialize and return a new mancala board
pub fn new() -> Self {
let init_hole = Hole {
marbles: STARTING_MARBLES,
};
let holes_a = [init_hole; NO_OF_HOLES_OF_EACH_PLAYER];
let holes_b = [init_hole; NO_OF_HOLES_OF_EACH_PLAYER];
let mancala_a = [Hole { marbles: 0 }];
let mancala_b = [Hole { marbles: 0 }];
Board {
holes_a,
holes_b,
mancala_a,
mancala_b,
}
}
// Take all marbles from the chosen hole and add them to the following holes and the player's mancala
// player: Player whos turn it is
// no: number of the selected hole. The numbering starts with 0 on the very left hole of the player whos turn it is
pub fn choose_hole(mut self, player: Player, no: usize) {
let (mut players_own_holes, other_players_holes, players_mancala) = match player {
Player::A => (self.holes_a, self.holes_b, self.mancala_a),
Player::B => (self.holes_b, self.holes_a, self.mancala_b),
};
let marbles_to_spend = players_own_holes[no].take_all() as usize;
let holes_iter = self
.holes_a
.iter_mut()
.chain(self.mancala_a.iter_mut())
.chain(self.holes_b.iter_mut())
.cycle()
.skip(no + 1)
.take(marbles_to_spend);
for mut hole in holes_iter {
hole.add_x(1);
}
}
}
However I get the following error:
error[E0277]: the trait bound `std::slice::IterMut<'_, Hole>: Clone` is not satisfied
--> src/lib.rs:75:14
|
75 | .cycle()
| ^^^^^ the trait `Clone` is not implemented for `std::slice::IterMut<'_, Hole>`
|
= note: required because of the requirements on the impl of `Clone` for `std::iter::Chain<std::iter::Chain<std::slice::IterMut<'_, Hole>, std::slice::IterMut<'_, Hole>>, std::slice::IterMut<'_, Hole>>`
note: required by a bound in `cycle`
--> /home/batman/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:3262:23
|
3262 | Self: Sized + Clone,
| ^^^^^ required by this bound in `cycle`
I also tried using the into_iter() method instead. I didn't get any errors then but the values of the holes did not change. I guess a copy was created and the method ran on the copy and then the copy went out of scope and it looked like nothing was changed.
The cycle() iterator method works internally by cloning the input iterator, iterating the clone until it returns None, and then replacing the clone with another clone of the input iterator. This requires that the input iterator can be cloned, but an iterator of mutable references to slice elements can't be cloned, because then you'd be able to call next() on the original and the clone and have two mutable references to the same value. This is supposed to be impossible in Rust, so std::slice::IterMut can't be cloned, and therefore you can't use .cycle() on it.
One way to solve this problem would be to alter your data structure. Arrays of one element generally indicate a design problem; you can accomplish the same thing with just a single value, anyway.
To make this problem simpler, just use a single array, with circular indices. Something like this:
| |12|11|10|09|08|07| |
|13|-----------------|06|
| |00|01|02|03|04|05| |
So now your data structure is simply struct Board { holes: [Hole; 14] }.
Traversing this data structure now becomes incredibly simple -- you can just do (0..14).cycle() to get a repeating iterator of array indices.
When using this data structure, we need to handle the game rule of skipping your opponent's mancala as marbles are distributed. We can handle this, along with handling which side of the board to start on, with a simple match, skip, and filter:
let (opponent_mancala_index, start_idx) = match player {
Player::A => (13, 0),
Player::B => (6, 7),
};
let indexes = (0..14)
.cycle()
.skip(no + start_idx)
.filter(|&v| v != opponent_mancala_index)
.take(marbles_to_spend);
for i in indexes {
self.holes[i].add_x(1);
}
You might consider making these special indexes named constants.
Also, note that your Board::choose_hole() function should take &mut self instead of mut self, otherwise you are making changes to a copy and then discarding that copy.

How can a local variable be assigned in previous iteration of loop

Just a question so I can better understand Rust.
Why does this fail:
for event in rulesengine::RuleEvent::into_enum_iter()
{
let mut event_stats: payloads::EventStats;
event_stats.name = rulesengine::RuleEvent::to_string(&event);
event_stats.event_count = event_counters.event_count[event as usize];
event_stats.event_participant_count = event_counters.event_participant_count[event as usize];
event_stats.event_trigger_count = event_counters.event_trigger_count[event as usize];
rules_stats.event_stats.push(event_stats);
}
With:
error[E0382]: assign to part of moved value: `event_stats`
--> src/motion.rs:317:37
|
316 | ... let mut event_stats: payloads::EventStats;
| --------------- move occurs because `event_stats` has type `payloads::EventStats`, which does not implement the `Copy` trait
317 | ... event_stats.name = rulesengine::RuleEvent::to_string(&event).clone();
| ^^^^^^^^^^^^^^^^ value partially assigned here after move
...
323 | ... },
| - value moved here, in previous iteration of loop
error: aborting due to previous error
When this works:
for event in rulesengine::RuleEvent::into_enum_iter()
{
let name = rulesengine::RuleEvent::to_string(&event);
let event_count = event_counters.event_count[event as usize];
let event_participant_count = event_counters.event_participant_count[event as usize];
let event_trigger_count = event_counters.event_trigger_count[event as usize];
let event_stats = payloads::EventStats { name, event_count, event_participant_count, event_trigger_count };
rules_stats.event_stats.push(event_stats);
}
or even better
for event in rulesengine::RuleEvent::into_enum_iter()
{
let event_stats = payloads::EventStats
{
name: rulesengine::RuleEvent::to_string(&event),
event_count: event_counters.event_count[event as usize],
event_participant_count: event_counters.event_participant_count[event as usize],
event_trigger_count: event_counters.event_trigger_count[event as usize],
};
rules_stats.event_stats.push(event_stats);
}
The variable event_stats is mutable yes, but local. It doesn't exist in the "previous iteration of loop" to be moved.
I believe the error is a mis-diagnosis of the problem. There have been compiler bugs in the past where the message incorrectly blames the loop for uninitialized variables. Perhaps this is another one of those cases.
The core issue is you are trying to assign a field to an uninitialized value:
let mut event_stats: payloads::EventStats;
event_stats.name = ...;
You have declared event_stats but have not initialized it. Rust does not allow you to piece-wise initialize a struct, at least not without a lot of extra juggling.
Both your working solutions create event_stats from a struct literal:
let event_stats = payloads::EventStats { name: ..., ... };
which is the correct way to initialize a struct.

Passing an immutable reference when a mutable reference exists

I have a for loop that iterates over a slice of Point structs. The Points will have some fields modified in the loop, so the function containing the loop requires a mutable reference to the slice.
The problem arises when I need to pass a (immutable) reference pointing to the slice to a function within the for loop that iterates over the mutable reference:
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut grid = vec![];
grid.push(Point { x: 10, y: 10 });
grid.push(Point { x: -1, y: 7 });
calculate_neighbors(&mut grid);
}
fn calculate_neighbors(grid: &mut [Point]) {
for pt in grid.iter_mut() {
pt.x = nonsense_calc(grid);
}
}
#[allow(unused_variables)]
fn nonsense_calc(grid: &[Point]) -> i32 {
unimplemented!();
}
Playground
error[E0502]: cannot borrow `*grid` as immutable because it is also borrowed as mutable
--> src/main.rs:18:30
|
17 | for pt in grid.iter_mut() {
| ---------------
| |
| mutable borrow occurs here
| mutable borrow used here, in later iteration of loop
18 | pt.x = nonsense_calc(grid);
| ^^^^ immutable borrow occurs here
The compiler complains that grid cannot be borrowed as immutable, because a mutable borrow already exists. This is correct, and I can see the problem it is trying to prevent, but how do I achieve what I need to do? Ideally, I do not have to create a copy of the grid, as this can be expensive.
A solution to avoid borrow the array for the iteration would be to use indexes:
fn calculate_neighbors(grid: &mut [Point]) {
for i in 0..grid.len() {
grid[i].x = nonsense_calc(grid);
}
}

Convert [type_a] to [type_b]

I've actually googled this extensively, within stackoverflow and elsewhere.
Most questions are about [UInt8] to String or [UInt8] to type_a (not array).
To clarify, I'd like to take an array of type_a. Get its pointer and tell swift to treat the next n iterations of type_b (size_of) as array of type_b.
I've tried variations of https://stackoverflow.com/a/26954091/5276890 which didn't work. A comment there led me to https://stackoverflow.com/a/42255468/5276890.
withMemoryRebound seems like the right way but I couldn't find the right invocation.
Here's a sample code of what I'm doing instead to convert [UInt8] to [UInt32.bigEndian], both to clarify and in case it's useful (not likely)
var intData = [UInt32]()
let M = UInt32(256*256*256)
var m = M
var bigE:UInt32 = 0
for i in 0..<data.count {
bigE += UInt32(data[i]) * m
if m == 1 {
intData.append(bigE)
bigE = 0
m = M
} else {
m = m/256
}
}
<disclaimer+rant>
I have to admit I never could figure out the whole closures+withUnsafe* syntax and mostly used patterns online and modified them. I'd spend the time learning this, just as soon as the language authors decide and settle down on one specific syntax :(
</disclaimer+rant>
Use withUnsafeBufferPointer to get a pointer to the element
storage of the source array.
Use withMemoryRebound to "reinterpret" that pointer as pointing
to elements of the target type.
Use Array(UnsafeBufferPointer(...) to create an array of the
target type.
Example:
let source: [UInt16] = [1, 2, 3, 4]
let dest = source.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: UInt32.self, capacity: 2) {
Array(UnsafeBufferPointer(start: $0, count: 2))
}
}
print(dest) // [131073, 262147]
Or as a generic function:
func convertArray<S, T>(_ source: [S], to: T.Type) -> [T] {
let count = source.count * MemoryLayout<S>.stride/MemoryLayout<T>.stride
return source.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: T.self, capacity: count) {
Array(UnsafeBufferPointer(start: $0, count: count))
}
}
}
Example:
let source: [UInt16] = [1, 2, 3, 4]
let dest = convertArray(source, to: UInt32.self)
print(dest) // [131073, 262147]
If you only need a (temporary) view on the array storage interpreted
in another type then you can avoid the Array creation
and use the UnsafeBufferPointer (which is a Collection and
has array-like methods) without copying the data:
source.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: UInt32.self, capacity: 2) {
let u32bufptr = UnsafeBufferPointer(start: $0, count: 2)
// ... Operate on u32bufptr ...
for elem in u32bufptr { print(elem) }
}
}

Parallel computing of array elements in rust [duplicate]

This question already has answers here:
Processing vec in parallel: how to do safely, or without using unstable features?
(2 answers)
Closed 7 years ago.
I'm novice to Rust (v1.0.0) and thread-programming.
I try to calculate elements of b-array using a-array. Each element of the b-array can be calculated independently of the others (parallel).
extern crate rand;
use rand::Rng;
use std::io;
use std::thread;
use std::sync::{Arc, Mutex};
fn main() {
let mut a : [u32; 10] = [0; 10];
let mut b = Arc::new(Mutex::new([0; 10]));
let mut rng = rand::thread_rng();
for x in 0..9 {
a[x] = (rng.gen::<u32>() % 1000) + 1;
};
for x in 0..4 {
let b = b.clone();
thread::spawn(move || { let mut b = b.lock().unwrap();
for y in 0..4 {
b[x] += a[y] * a[y*2+1];
b[x+5] += a[y+1] * a[y*2];
}
});
};
thread::sleep_ms(1000);
for x in 0..a.len() {
println!("a({0})={1}, b({0})={2}", x, a[x], b[x]);
};
}
Can you help me:
if I use: let mut b = Arc::new(Mutex::new([u32; 10] = [0; 10])); -> I get error unresolved name 'u32'. Did you mean 'a'? How can I set the type of array element ?
thread::sleep_ms(1000) - It is so rudely. How can I check that all thread is finished?
How can I get back my calculated b[i] and/or gather thread-calculated b-arrays in the final one ? Now I've got error: cannot index a value of type 'alloc::arc::Arc<std::sync::mutex::Mutex<[u32; 10]>>'
Can I use only one b-array in memory and send into thread (using pointers) to calculating two elements of b-array?
Thank for solutions.
Working code is (I've modified it for show problem):
extern crate rand;
use rand::Rng;
use std::thread;
use std::sync::{Arc, Mutex};
fn main() {
let mut a : [u32; 10000] = [0; 10000];
let b = Arc::new(Mutex::new([0u32; 10]));
let mut rng = rand::thread_rng();
for x in 0..10000 {
a[x] = (rng.gen::<u32>() % 10) + 1;
};
for x in 0..5 {
let b = b.clone();
thread::spawn(move || { let mut b = b.lock().unwrap();
println!("thread {} started", x);
for y in 0..5000 {
b[x] += a[y] * a[y*2+1];
b[x+5] += a[y+1] * a[y*2];
};
b[x] += a[x];
b[x+5] -= a[x];
println!("thread {} finished", x);
});
};
thread::sleep_ms(1000);
for x in 0..10 {
println!("b({0})={1}", x, b.lock().unwrap()[x]);
};
}
The output is:
thread 1 started
thread 1 finished
thread 3 started
thread 3 finished
thread 0 started
thread 0 finished
thread 2 started
thread 2 finished
thread 4 started
thread 4 finished
b(0)=149482
...
b(9)=149065
Threads are processed step-by-step.
Note that the clone() method on the Arc object does not "clone" the array, simply it increments the reference counter of the Arc.
I think you are asking for a general strategy to process data in parallel in Rust. Your code lock the b array in each thread, so you have no parallel processing.
To do real parallel processing you would need a mutable access to the array without a lock on the entire array but you cannot do that in safe Rust.
To do that you have to use some sort of unsafe mechanism, such raw pointers.
This is a simple example to process a (non mutable) input vector into a (mutable) output vector concurrently:
use std::thread;
use std::sync::Arc;
fn main() {
let input = Arc::new([1u32, 2, 3, 4]);
let output = Arc::new([0; 4]);
let mut handles = Vec::new();
for t in 0..4 {
let inp = input.clone();
let out = output.clone();
let handle = thread::spawn(move || unsafe {
let p = (out.as_ptr() as *mut u32).offset(t as isize);
*p = inp[t] + (t as u32 + 1);
});
handles.push(handle);
}
for h in handles {
h.join().unwrap();
}
println!("{:?}", output);
}
You still need to use Arc to pass data into the threads and to have a proper lifetime management.
Then inside the thread you need to get a mutable pointer to the data (out.as_ptr() as *mut u32), then the item processed in that thread using the offset method.

Resources