How to lazily create map entry whose construction uses self in Rust

前端 未结 2 1673
别跟我提以往
别跟我提以往 2021-01-06 02:40

I\'m trying to implement a lazy-construction / memoized evaluation / caching idiom in Rust.

There\'s an outer type which has a bunch of data and an accessor method.

相关标签:
2条回答
  • 2021-01-06 02:53

    So, the primary motivation for me wanting to pass Thing as an argument to ContainedThing::create was to use Thing's API to help in the construction. However, it turns out that I'll want it to be borrowed mutably, because I need recursive memoized construction here, and that makes it a cycle problem.

    So, it looks like my separated check + insert logic is here to stay.

    0 讨论(0)
  • 2021-01-06 03:03

    The answer is that it depends on specifically which state you need access to in the or_insert_with closure. The problem is that or_insert_with definitely cannot have access to the map itself because the entry api takes a mutable borrow of the map.

    If all you need for ContainedThing::create is just the size of the map, then you'll just need to calculate the map size ahead of time.

    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)
            })
        }
    }
    

    I think the spirit of the question was more about general strategies, though, so let's assume there's some other state within Thing that is also required to create 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)
            })
        }
    }
    

    Whether that's really cleaner than your other solution of manually checking if self.map.contains_key(&key) is up for debate. Destructuring tends to be the strategy that I go for, though, to allow borrowing specific members of self instead of the entire struct.

    0 讨论(0)
提交回复
热议问题