Rust 如何借用参照单元格<;HashMap>;,找到一个键,并返回对结果的引用?

Rust 如何借用参照单元格<;HashMap>;,找到一个键,并返回对结果的引用?,rust,interior-mutability,Rust,Interior Mutability,我有一个RefCell,想借用表,找到一个键,然后返回对结果的引用: use std::cell::RefCell; use std::collections::HashMap; struct Frame { map: RefCell<HashMap<String, String>>, } impl Frame { fn new() -> Frame { Frame { map: RefCell::new(

我有一个
RefCell
,想借用表,找到一个键,然后返回对结果的引用:

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
借用时,您得到的引用比
RefCell
的生命周期短。这是因为引用的生存期受到
borrow()
返回的保护的限制。该保护确保在放弃该保护之前,没有其他人可以获取该值的可变引用

但是,您正在尝试返回一个值,而不保持一个保护的活动状态。如果
Frame
有一个方法接受了
&self
参数,但试图对映射进行变异(这在
RefCell
中是可能的-如果您不需要这样做,那么放弃
RefCell
并在变异映射的方法上写入
&mut self
),您可能会意外地破坏其他人引用的
字符串。这正是借用检查器设计用来报告的错误类型

如果映射值实际上是不可变的(即,您的类型不允许对映射值进行变异),您还可以在映射中将它们包装在
Rc
中。因此,您可以返回
Rc
的克隆(这仅克隆引用计数指针,而不是基础字符串),这将允许您在从函数返回之前释放映射上的借用

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 } impl帧{ fn查找(&self,k:&String)->选项{ self.map.borrow().get(k.map)(|x | x.clone()) } }
嘿,谢谢。这是一个很好的解释。我还在学习,所以我一直在和借书检查员搏斗,但这当然是有道理的。我想我不太明白为什么RefCell会起作用——为什么非RefCell版本不也会因为同样的原因导致问题?@MarcMiller:我想现在你已经意识到生锈是所有权和借贷的问题了。通常,这些在编译时被检查,但是有基于
不安全的
代码的构造存在于编译器中,并在运行时验证正确性
RefCell
就是这样一种构造,因此引入它会稍微改变规则;您可以将其视为单线程代码的读写互斥体。@matthieu-m:谢谢您的加入!麻烦你澄清一下好吗?你是说RefCell将其更改为一种writer锁,而不是reader锁,即使我没有进行借用?我真的不想在这里克隆我的返回值,因为我的对象可能相当大,所以我考虑可能做一些类似于ref计数的事情:RefCell@MarcMiller:如果您根本没有使用
borrow_mut()
,那么使用
RefCell
绝对没有意义。只需将
HashMap
直接存储在
框架中即可。如果要避免复制/克隆,请返回引用,这就是它们的用途@MarcMiller:
RefCell
本质上是一个读写器锁,尽管不是线程安全的;当您
.borrow\u mut()
时,它会检查其他人是否已拥有借用(写入程序需要独占访问权限),当您
.borrow()
时,它会检查没有写入程序处于活动状态(读卡器获得共享访问权限)。不过,为了让
RefCell
保证写入者的排他性,这意味着通过
.borrow()
借用的内容不能比防护装置(
Ref>的寿命长
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())
    }
}