How lazy to create a map whose design uses itself in Rust

I am trying to implement lazy-construction / memoized evaluation / caching idioms in Rust.

How it works, there is an external type that contains a bunch of data and an access method. The accessor method must either return the cached calculation (if any), or calculate it and save the return value on the map for later use. The value being cached does not need to reference an external value, so there is no circular reference problem; but he needs access to external meaning data in order to build himself.

Here's a complete example that fails Rust's borrowing test:

use std::collections::HashMap;
pub struct ContainedThing {
    count: usize,
}

impl ContainedThing {
    fn create(thing: &Thing) -> ContainedThing {
        // create uses an arbitrary number of attributes from Thing
        // it doesn't keep any references after returning though
        let count = thing.map.len();
        ContainedThing { count: count }
    }
}

struct Thing {
    map: HashMap<i32,ContainedThing>,
}

impl Thing {
    pub fn get(&mut self, key: i32) -> &ContainedThing {
        self.map.entry(key).or_insert_with(|| ContainedThing::create(&self))
    }
}

fn main() {
}

The specific error is this:

test.rs:20:44: 20:46 error: cannot borrow `self` as immutable because `self.map` is also borrowed as mutable [E0502]
test.rs:20         self.map.entry(key).or_insert_with(|| ContainedThing::create(&self))
                                                      ^~
test.rs:20:9: 20:17 note: mutable borrow occurs here
test.rs:20         self.map.entry(key).or_insert_with(|| ContainedThing::create(&self))
                   ^~~~~~~~
test.rs:21:5: 21:6 note: mutable borrow ends here
test.rs:21     }
               ^
test.rs:20:71: 20:75 note: borrow occurs due to use of `self` in closure
test.rs:20         self.map.entry(key).or_insert_with(|| ContainedThing::create(&self))
                                                                                 ^~~~

, . , get(), API entry(), : match self.

get :

pub fn get(&mut self, key: i32) -> &ContainedThing {
    if !self.map.contains_key(&key) {
        let thing = ContainedThing::create(&self);
        self.map.insert(key, thing);
    }
    self.map.get(&key).unwrap()
}

( unwrap) , , , .

  • - . entry(), , , .
  • . , .
  • unwrap; , .

, ?

+4
2

, , , or_insert_with. , or_insert_with , api .

, ContainedThing::create, , .

impl Thing {
    pub fn get(&mut self, key: i32) -> &ContainedThing {
        let map_size = self.map.len();
        self.map.entry(&key).or_insert_with(|| {
            // The call to entry takes a mutable reference to the map,
            // so you cannot borrow map again in here
            ContainedThing::create(map_size)
        })
    }
}

, , , Thing - , ContainedThing.

struct Thing {
    map: HashMap<i32, ContainedThing>,
    some_other_stuff: AnotherType //assume that this other state is also required in order to create ContainedThing
}

impl Thing {
    pub fn get(&mut self, key: i32) -> &ContainedThing {
        //this is the borrow of self
        let Thing {ref mut map, ref mut some_other_stuff} = *self; 

        let map_size = map.len();
        map.entry(key).or_insert_with(|| { // map.entry now only borrows map instead of self
            // Here you're free to borrow any other members of Thing apart from map
            ContainedThing::create(map_size, some_other_stuff) 
        })
    }
}

, if self.map.contains_key(&key). , , , , self .

+1

, , Thing ContainedThing::create, API Thing, . , , , , , .

, , check + insert , .

0

Source: https://habr.com/ru/post/1651556/


All Articles