Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to work around self borrowing with map .or_insert_with? Rust (1.11) [duplicate]

This snippet

use std::collections::HashMap;

struct Foo {
    local_ids: HashMap<i32, i32>,
    last_id: i32,
}

impl Foo {
    fn foo(&mut self, external_id: i32) {
        let id = self.local_ids
                     .entry(external_id)
                     .or_insert_with(||{self.last_id += 1; self.last_id});
    }
}

Doesn't work because we can't borrow self twice

error: closure requires unique access to `self` but `self.local_ids` is already borrowed [E0500]

Is this possible to fix without a second key lookup?


This is very similar to Rust: HashMap borrow issue when trying to implement find or insert, but the API has changed substantially.

The find_with_or_insert_with answer from above doesn't appear to map to the current api.

like image 838
mfarrugi Avatar asked Oct 24 '16 01:10

mfarrugi


1 Answers

The problem is that the closure captures self, whereas it only needs to capture a mutable reference to the last_id field.

Rust allows us to take independent mutable borrows on distinct fields, so we can use that to our advantage and pass a mutable reference to the last_id field to the closure.

use std::collections::HashMap;

struct Foo {
    local_ids: HashMap<i32, i32>,
    last_id: i32,
}

impl Foo {
    fn foo(&mut self, external_id: i32) {
        let last_id = &mut self.last_id;
        let id = self.local_ids
                     .entry(external_id)
                     .or_insert_with(|| { *last_id += 1; *last_id });
    }
}

When we use the expression self.last_id in the closure, the closure captures self directly, but Rust doesn't realize that the borrows are independent, so we need to be more explicit.

like image 56
Francis Gagné Avatar answered Nov 03 '22 05:11

Francis Gagné