I'm working on today's Advent of Code puzzle and it involves modeling how a few processes run, feeding input into the next process in a circular chain, and getting a result out of it once all the processes halt. I've been working in Rust for this, and have come to a problem with the borrowchecker that I don't understand how to get past.
A prior level modeled only a single pass of this chaining shape, forwarding the result from one process to the next one, and eventually stopping with the last process and waiting for it to finish:
A -> B -> C -> {answer}
Now I have to forward the output from one process to the next, looping back into the first process many times until the whole system stops
+-> A -> B -> C -+
^ |
+---<----<-------+ = = => {answer, when system stops}
When I was modelling the one-way chain, I could use vectors for the pipes and advance the process by calling a function that mutates the vectors, pulling data off the input pipe and pushing data onto the output pipe, which would become the input pipe for the next process:
// run processes piping in a line, a -> b -> c
next_step(&mut process_a_state, &mut stdin_a, &mut stdin_b);
next_step(&mut process_b_state, &mut stdin_b, &mut stdin_c);
next_step(&mut process_c_state, &mut stdin_c, &mut stdin_a);
My plan for modelling the ring of processes is to keep a Vec
of tuples that contain references to the process states and pipes, and then iterate over them until the processes signal that they should no longer be called:
// run processes piping ring: +-> a -> b -> c -+
// | |
// +---<----<----<--+
let mut schedule_ring = vec![
(process_a_state, &stdin_a, &stdin_b),
(process_b_state, &stdin_b, &stdin_c),
(process_c_state, &stdin_c, &stdin_a),
];
loop {
let (mut current_state, mut pipe_in, mut pipe_out) = schedule_ring.remove(0);
let should_we_stop = next_step(&mut current_state, &mut pipe_in, &mut pipe_out);
schedule_ring.push((current_state, pipe_in, pipe_out));
if should_we_stop {
break;
}
}
However, this throws a lot of compiler errors about references and mutable references. I haven't figured out the right way to throw in Rc
's or mutable references into my ring of processes to get this to compile. I'm not sure what is available for me to use, nor if I should be changing the design of my model to avoid the tricky memory stuff I'm trying to accomplish in Rust right now.
I have what I think is a minimal toy example that demonstrates how process state can be advanced, and how I've currently designed my pipes. This should be able to all run in a single thread, although processes may need to skip if they are blocked reading from their stdin pipe if the process in front of them does not currently have input for it to consume. I was hoping that since this is all single threaded I could store my process states and pipe references in a queue that cycles through all the processes, but I'm missing how to use Rust correctly here.