0

I wanted something like this:

struct Node {
    outbound_mailboxes: Vec<u32>,       // memory used for inter-node comms    
    inbound_links: Vec<(usize, usize)>, // (source node index, source node outbound mailbox index)
}

fn node_action(inputs: Vec<&u32>, outputs: Vec<&mut u32>) {  // keeping this signature is important
    println!("{:?}, {:?}", inputs, outputs);
    // more code: think about the inputs;  write to the outputs
}

fn main() {
    // 3 nodes bidirectionally connected 0--1 and 0--2
    let mut nodes = vec![
        Node { outbound_mailboxes: vec![0, 1], inbound_links: vec![(1, 0), (2, 0)] },
        Node { outbound_mailboxes: vec![2],    inbound_links: vec![(0, 0)] },
        Node { outbound_mailboxes: vec![3],    inbound_links: vec![(0, 1)] },
    ];

    // trigger node 0 action
    let mut inputs: Vec<&u32> = Vec::new();
    for (i, j) in &nodes[0].inbound_links {
        inputs.push(&nodes[*i].outbound_mailboxes[*j]);
    }
    let outputs: Vec<&mut u32> = nodes[0].outbound_mailboxes.iter_mut().map(|m| m).collect();
    node_action(inputs, outputs);
}

It did not work:

error[E0502]: cannot borrow `nodes` as mutable because it is also borrowed as immutable
  --> src/main.rs:24:34
   |
22 |         inputs.push(&nodes[*i].outbound_mailboxes[*j]);
   |                      ----- immutable borrow occurs here
23 |     }
24 |     let outputs: Vec<&mut u32> = nodes[0].outbound_mailboxes.iter_mut().map(|m| m).collect();
   |                                  ^^^^^ mutable borrow occurs here
25 |     node_action(inputs, outputs);
   |                 ------ immutable borrow later used here

Shepmaster suggested that How to get mutable references to two array elements at the same time? already answered my question. With Shepmaster's additional comments I now understand why (it was not clear to me researching that answer prior to posting).

Thank you very much Shepmaster -- your patience is greatly appreciated; apologies for not understanding more quickly.

Refactored code that looks to fully meet my needs:

struct Node {
    outbound_mailboxes: Vec<u32>,       // memory used for inter-node comms
    inbound_links: Vec<(usize, usize)>, // (source node index, source node outbound mailbox index)
}

fn node_action(inputs: &[&u32], outputs: &[&mut u32]) {
    println!("{:?}, {:?}", inputs, outputs)
}

fn trigger_action(nodes: &mut [Node], target_node_idx: usize) {
    assert!(target_node_idx < nodes.len());
    let (left_nodes, rest) = nodes.split_at_mut(target_node_idx);
    let (target_node, right_nodes) = rest.split_first_mut().unwrap();
    let outputs: Vec<&mut u32> = target_node.outbound_mailboxes.iter_mut().collect();
    let mut inputs: Vec<&u32> = Vec::new();
    for (i, j) in &target_node.inbound_links {
        assert_ne!(*i, target_node_idx);
        let node = {
            if *i < target_node_idx { &left_nodes[*i] }
            else { &right_nodes[*i - (target_node_idx + 1)] }
        };
        inputs.push(&node.outbound_mailboxes[*j]);
    }
    node_action(&inputs, &outputs)
}

fn main() {
    // 3 nodes bidirectionally connected 0--1 and 0--2
    let mut nodes = vec![
        Node { outbound_mailboxes: vec![0, 1], inbound_links: vec![(1, 0), (2, 0)] },
        Node { outbound_mailboxes: vec![2],    inbound_links: vec![(0, 0)] },
        Node { outbound_mailboxes: vec![3],    inbound_links: vec![(0, 1)] },
    ];

    trigger_action(&mut nodes, 0); // prints "[2, 3], [0, 1]"
    trigger_action(&mut nodes, 1); // prints "[0], [2]"
    trigger_action(&mut nodes, 2); // prints "[0], [1]"
}
oddcowboy
  • 73
  • 5
  • Your question may be answered by [How to get mutable references to two array elements at the same time?](https://stackoverflow.com/q/30073684/155423) – Shepmaster May 12 '21 at 19:12
  • I thought about that carefully before posting and tried to indicate why with the commentary around disjoint mailboxes -- but maybe was insufficiently clear. Using split_at_mut() would seem to require vectors that could be split, which in the general case is not what we have (assume we want to call node_action() for each of the nodes). Are you suggesting a refactor to sort the node list prior to each call to node_action to split out the node for which we want access to the outbound_mailboxes? If we did that, it would scramble the inbound_link indices (we have these b/c normal arena reasons). – oddcowboy May 12 '21 at 19:29
  • I linked to a question that has _6_ answers, some with multiple suggestions, only one of which is `split_at_mut`. One of the others is `get_unchecked_mut`, which seemingly meets all of the criteria you've laid out. – Shepmaster May 12 '21 at 19:31
  • *which in the general case is not what we have* - it's very hard (and exhausting!) to answer questions where the requirements slowly change with each new suggestion. I'd encourage you to put _all_ of the requirements you have into the question. – Shepmaster May 12 '21 at 19:34
  • *require vectors that could be split* - why couldn't they be split? You can do something like `for i in 0..things.len() { split_at(i) }` – Shepmaster May 12 '21 at 19:35
  • Thank you! Things make much more sense now. – oddcowboy May 13 '21 at 05:28

0 Answers0