如何借用 RefCell<HashMap>、找到键并返回对结果的引用? [复制]

Posted

技术标签:

【中文标题】如何借用 RefCell<HashMap>、找到键并返回对结果的引用? [复制]【英文标题】:How do I borrow a RefCell<HashMap>, find a key, and return a reference to the result? [duplicate] 【发布时间】:2015-07-28 16:28:14 【问题描述】:

我有一个RefCell&lt;HashMap&gt;,想借表,找个key,返回一个对结果的引用:

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!"));

(playground)

如果我删除 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 的寿命短。这是因为引用的生命周期受到borrow() 返回的守卫的限制。该守卫确保在解除守卫之前没有其他人可以对该值进行可变引用。

但是,您正在尝试返回一个值而不让守卫保持活动状态。如果Frame 有一个采用&amp;self 参数但试图改变地图的方法(RefCell 是可能的——如果你不需要这样做,那么放弃RefCell 并写&amp;mut self在改变地图的方法上),您可能会不小心破坏其他人引用的String。这正是借用检查器旨在报告的错误类型!

如果地图值实际上是不可变的(即您的类型不允许改变地图的值),您也可以将它们包装在地图中的 Rc 中。因此,您可以返回 Rc&lt;String&gt; 的克隆(这只克隆引用计数指针,而不是底层字符串),这样您就可以在从函数返回之前释放映射上的借用。

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

【讨论】:

嘿,谢谢。这是一个很好的解释。我还在学习,所以我一直在和借用检查器搏斗,但这当然是有道理的。我想我不太明白为什么 RefCell 会有所作为——为什么非 RefCell 版本也不会因为同样的原因而导致问题? @MarcMiller:我想现在你已经意识到 Rust 就是关于所有权和借用的。通常,这些会在编译时进行检查,但是有一些基于 unsafe 代码的构造存在于编译器中,而是在运行时验证正确性。 RefCell 就是这样一个结构,因此引入它会稍微改变规则;您可以将其视为单线程代码的读写互斥体。 @matthieu-m:感谢您的参与!我能麻烦你澄清一下吗?您是说 RefCell 将其更改为一种写入器锁,而不仅仅是一个读取器锁,即使我没有做borrow_mut?我真的不想在这里克隆我的返回值,因为我的对象可能非常大,所以我正在考虑可能做一些类似引用计数的事情: RefCell> --但这让我觉得我做错了什么。谢谢。 @MarcMiller:如果您根本不使用borrow_mut(),那么使用RefCell 绝对没有意义。只需将HashMap 直接存储在您的Frame 中即可。如果您想避免复制/克隆,则返回引用,这就是他们的目的! @MarcMiller: RefCell 本质上是一个读写锁,虽然不是线程安全的;当你 .borrow_mut() 它检查没有其他人已经借用(作者需要独占访问),当你 .borrow() 它检查没有作者处于活动状态(读者获得共享访问)。但是,为了让RefCell 保证作者的排他性,这意味着您通过.borrow() 借用的东西不能比守卫(Ref&lt;'a, T&gt;)活得更久,这就是您在这里所经历的(因为守卫不会活得更久)比你的功能)。返回Rc&lt;String&gt; 建议解决此问题。

以上是关于如何借用 RefCell<HashMap>、找到键并返回对结果的引用? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何正确访问 RefCell 中的值

如何将 Rc<RefCell<_>> 中的具体对象转换为动态特征对象? [复制]

如何将 Rc<RefCell<dyn T>> 传递给想要 &dyn T 的 fn?

Rayon 如何防止线程之间使用 RefCell<T>、Cell<T> 和 Rc<T>?

Rc<RefCell<T>> 和 RefCell<Rc<T>> 有啥区别?

Rc<RefCell<T>> 和 RefCell<Rc<T>> 有啥区别?