Получить HashMap обратно от Entry?

Я реализую кеш, который пытается выполнить поиск в таблице, если это не удается, он пытается получить значение простым методом, а если это не удается, он идет и вычисляет несколько новых записей в кеше. Система Entry кажется специально разработанной для первой половины этого, но я не могу заставить средство проверки заимствования позволить мне завершить вторую половину.

use std::collections::HashMap;
fn main() {
    let mut cache = Cache { cache: HashMap::new() };
    println!("{}", cache.get_from_cache(10));

}


struct Cache {
    cache: HashMap<u32, String>
}
impl Cache {
    fn get_from_cache<'a>(&'a mut self, i: u32) -> &'a String {
        match self.cache.entry(i) {
            std::collections::hash_map::Entry::Occupied(entry) => return entry.into_mut(),
            std::collections::hash_map::Entry::Vacant(entry) => {
                // Some values have an easy way to be computed...
                if i == 5 {
                    return entry.insert("my string".to_string())
                }
            }
        }

        // Neither look-up method succeeded, so we 'compute' values one-by-one
        for j in 1..=i {
            self.cache.insert(j, "another string".to_string()); // Borrow checker fails here
        }
        self.cache.get(&i).unwrap()
        
    }
    
}

Проблема в том, что Entry у self.cache.entry(i) заимствует self.cache на всю жизнь 'a, хотя мне это больше не нужно в тот момент, когда я пытаюсь это сделать self.cache.insert.

Одним из решений этого (я думаю) было бы, если бы был способ превратить мою ссылку на Entry в ссылку на его HashMap, а затем вставить через эту ссылку. Однако я не вижу возможности сделать это с интерфейсом entry. Есть ли способ добиться этого? Или иным образом удовлетворить проверку заимствования?

entry API существует, чтобы избежать подобных проблем, вызванных ограничениями средства проверки заимствования. Я думаю, вам придется использовать избыточную проверку contains_key в качестве обходного пути на данный момент.

PitaJ 22.11.2022 20:32
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
1
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это легко исправить, отделив вставку значений от возврата конечного результата. Вы можете сначала убедиться, что значение находится в кеше или вставить его с помощью какой-либо стратегии, если нет, а затем вернуть новое значение (теперь оно гарантированно будет в HashMap):

fn get_from_cache<'a>(&'a mut self, i: u32) -> &'a String {
    // handle inserting the value if necessary:
    match self.cache.entry(i) {
        std::collections::hash_map::Entry::Occupied(entry) => (),
        // Some values have an easy way to be computed...
        std::collections::hash_map::Entry::Vacant(entry) if i == 5 => {
            entry.insert("my string".to_string());
        }
        // ... others aren't
        std::collections::hash_map::Entry::Vacant(entry) => {
            for j in 1..=i {
                self.cache.insert(j, "another string".to_string());
            }
        }
    }

    // The value is now definitely in `self.cache` so we can return a reference to it
    &self.cache[&i]
    
}
&self.cache[&i] по сути такой же, как self.cache.get(&i).unwrap(), и имеет избыточную проверку, которая может быть оптимизирована, а может и нет.
PitaJ 22.11.2022 21:55

@PitaJ Правда? Глядя на исходный код, кажется, что index просто вызывает self.get(key).expect(...).

isaactfa 22.11.2022 22:20
expect — это просто unwrap с указанным сообщением.
PitaJ 22.11.2022 22:22

Да, так что я не понимаю, в чем должна быть разница между &self.cache[&i] и self.cache.get(&i).unwrap()?

isaactfa 22.11.2022 22:24

Я думаю, произошло недоразумение. Моя точка зрения заключалась именно в том, что они одинаковы и что ОБА вводят избыточную проверку наличия записи под &i. В этой избыточной проверке не было бы необходимости, если бы средство проверки заимствования не было ограничено.

PitaJ 22.11.2022 22:29

Ах, извините, теперь я понимаю, что вы имеете в виду. Я не имел в виду, что индексирование не будет выполнять проверку, которую делает get (я просто подумал, что это выглядит чище), но я понимаю, почему вы так думаете. Да, очевидно, было бы неплохо просто вернуть ссылку, когда мы уже проверили, что там есть значение. Когда-нибудь, Полоний, когда-нибудь...

isaactfa 22.11.2022 22:33

Так почему же entry все еще не имеет изменяемой ссылки на self.cache в момент выполнения self.cache.insert в этом примере? В этом случае NLL смог сделать вывод, что после этого запись не использовалась? Разочаровывает то, что решение в основном «не используйте entry», хотя Entry было создано для проблем, очень похожих на эту.

tgbrooks 23.11.2022 12:59

Другие вопросы по теме