How to iterate a vector twice with mutable and immutable borrow?

Hi guys, the code was

use std::collections::HashMap;

fn main() {
    let mut array = vec![String::from("1"),String::from("2"),String::from("3")];
    let mut map:HashMap<&str,&str> = HashMap::new();
    {
        for item in array.iter() {
            map.insert(item.as_str(), item.as_str());
        }
    }
    for item in array.iter_mut() {
        map.get(item.as_str());
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `array` as mutable because it is also borrowed as immutable
  --> src/main.rs:11:17
   |
7  |         for item in array.iter() {
   |                     ------------ immutable borrow occurs here
...
11 |     for item in array.iter_mut() {
   |                 ^^^^^^^^^^^^^^^^ mutable borrow occurs here
12 |         map.get(item.as_str());
   |         ---------------------- immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` (bin "playground") due to previous error


I can solve this by changing

let mut map:HashMap<&str,&str> = HashMap::new();

to

let mut map:HashMap<String,String> = HashMap::new();

But I'm wondering if there is another approach with is more efficient like Zero-Copy

Thanks in advance!

Not in the general case. Mutable references in Rust guarantee exclusive access, so it's impossible to make one while you have an immutable reference to the same thing stored somewhere (here, inside map).

It's likely, however, that there is a more efficient way to accomplish your specific task. Without more details of what you're trying to accomplish, though, it's hard to give a concrete suggestion.

2 Likes

There might be other solutions depending on what you need those mutable references for, but your code doesn't show that (in fact the fix for your code is to change .iter_mut() to .iter(), but my guess is that in your actual code you can't do that)

1 Like

Taking the borrows out of the array.

    let a = [String::from("1"),String::from("2"),String::from("3")];
    let mut array = vec![&a[0],&a[1],&a[2]];

Can even have the Vec as Option<&str> or something else.

Hi,thanks for replying

my bad cases were:

1st case:

    let mut nodes: Vec<(&str, String)> = Vec::with_capacity(32);
    let mut temp_node_ids:Vec<String> = Vec::with_capacity(8);
    match node {
        Node::A(n) => {
            let node = NewANode {};
            // new node uses node_id of A
            nodes.push((n.node_id.as_str(), String::new()));
        }
        Node::B(n) => {
            let mut cnt = 1u8;
            for b in n.array.iter() {
                let new_node_id = if cnt == 1 {
                    n.node_id.clone()
                } else {
                    format!("{}-{}", &n.node_id, cnt)
                };
                let idx = temp_node_ids.len();
                temp_node_ids.push(node_id);// mutable borrow here
                cnt = cnt + 1;
                let node = NewBNode {};
                nodes.push((temp_node_ids[idx].as_str(), bytes));// immutable borrow here
            }
        }
        Node::C(n) => {
            let node = NewCNode {};
            // new node uses node_id of C
            nodes.push((n.node_id.as_str(), String::new()));
        }
        Node::D(n) => {
            let node = NewDNode {};
            // new node uses node_id of D
            nodes.push((n.node_id.as_str(), String::new()));
        }
    };

Error's

205 |                 temp_node_ids.push(node_id);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
213 |                     nodes.push((temp_node_ids[idx].as_str(), bytes));
    |                     ------------------------------------------------
    |                     |           |
    |                     |           immutable borrow occurs here
    |                     immutable borrow later used here

2nd case:

    let mut branches_link: HashMap<&str, &str> = HashMap::with_capacity(32);
    let mut node_cnt = 0usize;
    for n in cells.iter() {
        if n.type.eq("line") {
            let source = n.extra.get("source");
            let port = source.as_ref().unwrap().get("port");
            let target = n.extra.get("target");
            let cell = target.as_ref().unwrap().get("cell");
            branches_link.insert(
                port.unwrap().as_str().unwrap(),
                cell.unwrap().as_str().unwrap(),
            );
        } else {
            if n.data.is_none() {
                return Err(Error::ErrorWithMessage(String::from("Data's missing")));
            }
            node_cnt = node_cnt + 1;
        }
    }
    let mut nodes: Vec<&mut Node> = Vec::with_capacity(node_cnt);
    for n in cells.iter_mut() {
        if let Some(node) = n.data.as_mut() {
            if let Some(mut branches) = node.get_branches() {
                for branch in branches.iter_mut() {
                    if branch.branch_id.is_empty() {
                        return Err(Error::ErrorWithMessage(String::from("Data's missing")));
                    }
                    let target_node_id = branches_link.remove(branch.branch_id.as_str());
                    if let Some(t) = target_node_id {
                        branch.target_node_id = String::from(t);
                    } else {
                        return Err(Error::ErrorWithMessage(String::from("Data's missing")));
                    }
                }
            }
            nodes.push(node);
        }
    }

Error's

    |
133 |     for n in cells.cells.iter() {
    |              ------------------ immutable borrow occurs here
...
152 |     for n in cells.cells.iter_mut() {
    |              ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
159 |                     let target_node_id = branches_link.remove(branch.branch_id.as_str());
    |                                          ----------------------------------------------- immutable borrow later used here

In both cases you seem to be using strings for some kind of ids in a graph, and you get an error when you try to hold an id that's stored inside the graph/data structure while also trying to modify said graph/data structure. A common solution for this problem is to not use string ids for nodes, and instead use an integer id (or anything cheaply copyable, integers are just the easiest to use). If your domain problem uses strings ids you can map them to temporary integer ids with a string-to-integer map.

3 Likes

An alternative to integer ids is to store all of the id strings in a typed-arena or specialized string-interning collection, so that the lifetimes of the &strs are independent of the graph structure you're using them in.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.