Как мне избежать разворачивания опции, возвращенной при доступе к хэш-карте с параметром в качестве значения?

#rust #hashmap

#Ржавчина #хэш-карта

Вопрос:

Я следую вводной книге. В разделе 13.1 авторы реализовали «кэшер» и попросили читателя реализовать его с HashMap помощью . Я заставил его работать, но я не совсем доволен из-за вызова unwrap() , который кажется немного неуместным.

В частности, в Cacher::value() , Some(v) => v.unwrap() вызов — это то, что меня не устраивает — учитывая документацию unwrap() состояний, которые следует избегать вызовов функции. Какое лучшее / более идиоматическое предложение для выполнения того, что я пытаюсь выполнить?

Код:

 use std::collections::HashMap;

struct Cacher<T> {
    calculation: T,
    hmap: HashMap<u32, Option<u32>>,
}

impl<T> Cacher<T>
where
    T: Fn(u32) -> u32,
{
    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            hmap: HashMap::new(),
        }
    }

    fn value(amp;mut self, arg: u32) -> u32 {
        match self.hmap.get(amp;arg) {
            Some(v) => v.unwrap(),
            None => {
                let v = (self.calculation)(arg);
                self.hmap.insert(arg, Some(v));
                v
            }
        }
    }
}

fn main() {
    let mut cache = Cacher::new(|x| x * x);
    let v = vec![cache.value(1), cache.value(2), cache.value(3)];
    println!("v: {:?}", v);
}
 

Комментарии:

1. Зачем иметь Option in HashMap<u32, Option<u32>> ?

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

3. Если вы хотите сохранить значения как Option<u32> , вы можете рассматривать no entry и None запись как одинаковые, сопоставляя только on Some(Some(v)) : playground

Ответ №1:

Нет необходимости иметь Option в HashMap<u32, Option<u32>> , потому HashMap что само по себе может представлять, кэшировано значение или нет. Новый и улучшенный код:

 use std::collections::HashMap;

struct Cacher<T> {
    calculation: T,
    hmap: HashMap<u32, u32>,
}

impl<T> Cacher<T>
where
    T: Fn(u32) -> u32,
{
    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            hmap: HashMap::new(),
        }
    }

    fn value(amp;mut self, arg: u32) -> u32 {
        match self.hmap.get(amp;arg) {
            Some(v) => *v,
            None => {
                let v = (self.calculation)(arg);
                self.hmap.insert(arg, v);
                v
            }
        }
    }
}

fn main() {
    let mut cache = Cacher::new(|x| x * x);
    let v = vec![cache.value(1), cache.value(2), cache.value(3)];
    println!("v: {:?}", v);
}
 

Комментарии:

1. Далее в вашем путешествии вы можете заглянуть в entry API для HashMap . Как написано, когда значение не найдено, вы вычисляете его, а затем вставляете, что требует дублирования работы по хешированию ключа и выяснения, куда он идет. С помощью API ввода вы получаете слот один раз заранее, а затем решаете, что делать, исходя из того, заполнен ли он.

2. Спасибо за совет! Я обязательно изучу это, когда решу углубиться в более сложные вещи!