Как заимствовать RefCell <HashMap>, найти ключ и вернуть ссылку на результат?

У меня есть RefCell<HashMap> и я хочу позаимствовать таблицу, найти ключ и вернуть ссылку на результат:

use std::cell::RefCell;
use std::collections::HashMap;

struct Frame {
    map: RefCell<HashMap<String, String>>,
}

impl Frame {
    fn new() -> Frame {
        Frame {
            map: RefCell::new(HashMap::new()),
        }
    }

    fn lookup<'a>(&'a self, k: &String) -> Option<&'a String> {
        self.map.borrow().get(k)
    }
}

fn main() {
    let f = Frame::new();
    println!("{}", f.lookup(&"hello".to_string()).expect("blargh!"));
}

(детская площадка)

Если я RefCell тогда все работает хорошо:

struct Frame {
    map: HashMap<String, String>,
}

impl Frame {
    fn lookup<'a>(&'a self, k: &String) -> Option<&'a String> {
        self.map.get(k)
    }
}

Как правильно написать функцию поиска без копирования строки в хеш-таблицу?

Ответ 1

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

Однако вы пытаетесь вернуть значение, не поддерживая охрану. Если у Frame был метод, который принял аргумент &self но пытался изменить карту (что возможно с помощью RefCell - если вам не нужно это делать, RefCell и напишите &mut self на методы, которые изменяют карту), Вы можете случайно уничтожить String, на которую ссылается кто-то другой. Это именно тот тип ошибок, о котором разработчик заимствований должен был сообщить!

Если значения карты являются фактически неизменяемыми (т.е. Ваш тип не позволяет изменять значения карты), вы также можете обернуть их в Rc на вашей карте. Поэтому вы можете вернуть клон Rc<String> (это только клонирует указатель с подсчетом ссылок, а не нижележащую строку), что позволит освободить заимствование на карте перед возвратом из функции.

struct Frame {
    map: RefCell<HashMap<String, Rc<String>>>
}

impl Frame {
    fn lookup(&self, k: &String) -> Option<Rc<String>> {
        self.map.borrow().get(k).map(|x| x.clone())
    }
}