Why are this lines of codes rejected by borrowing checker

#1
fn find<'a>(acc: &str, parents: &'a HashMap<&str, &str>) -> &'a str {
    let parent = parents.get(acc).unwrap();
    if *parent == acc {
        *parent
    } else {
        find(parent, parents)
    }
}

main() {
    let accts: Vec<Vec<String>> = vec![]; // some data
    let mut parents = HashMap::<&str, &str>::new();

    for ac_list in &accts {
        for email in &ac_list[1..] {

            let p = find(email, &parents);
            let p2 = find(&ac_list[1], &parents);

            parents.insert(p, p2); // cannot borrow `parents` as mutable because it is also borrowed as immutable
        }
    }
}

I thought the immutable borrowing &parents are temporary references, so the mutable borrowing should pass. Is my understanding somehow wrong?

#2

They are not temporary references, they live until they’re dropped. And they can’t be dropped before insert takes place, since they’re arguments to it.

#3

I don’t understand why &parents references live until the .insert call. Shouldn’t they be dropped when find calls are finished?

#4

&parents is not the problem.
As stated in find's signature the returned value borrows from parents which means parents is immuatble borrowed at least as long as the returned value is not droped.
Therefore, parents is still borrowed by p and p2 when you want to insert them. So mutable borrow of parents is not allowed.

3 Likes
#5

Thanks!

#6

Change

fn find<'a>(acc: &str, parents: &'a HashMap<&str, &str>) -> &'a str {

(where the lifetime parameters are saying that your returned str reference is borrowing from the HashMap)
to

fn find<'a>(acc: &str, parents: &HashMap<&str, &'a str>) -> &'a str {

(where the lifetime parameters are saying that your returned str reference is “equivalent” (in your code, a copy) to the values of your HashMap, which may match your intuition).

5 Likes
#7

This is extremely helpful !

1 Like