如何将引用 return 引用到 Rc<RefCell<>> 函数参数中?

How to return a reference into a Rc<RefCell<>> function argument?

这是从我正在编写的解释器中获取的最小可重现错误。据我了解,我应该能够 return 对 RefCell 中结构字段的引用,因为 RefCell 具有足够的生命周期。但是,编译器告诉我我不能 return 对当前函数拥有的值的引用,坦率地说这让我感到困惑。

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

#[derive(Debug)]
enum Value {
    Number,
    String,
}

struct Object {
    pub properties: HashMap<String, Value>,
}

impl Object {
    pub fn get_property(&mut self, name: &str) -> Option<&mut Value> {
        self.properties.get_mut(name)
    }
}

fn get_property(global_object_rcc: Rc<RefCell<Object>>, name: &str) -> Option<&mut Value> {
    // Rust cannot verify that this Rc isn't the last Rc that just got moved into this function?
    global_object_rcc.borrow_mut().get_property(name)
}

fn main() {
    // Construct global object
    let mut global_object = Object {
        properties: HashMap::new(),
    };

    // Give it a property
    global_object
        .properties
        .insert("Test".to_owned(), Value::Number);

    // Put it in a Rc<RefCell> (rcc) for sharing
    let global_object_rcc = Rc::new(RefCell::new(global_object));

    // Get a reference to its property, should be valid because the reference only needs to live
    // as long as the global_object
    let property = get_property(global_object_rcc, "Test");

    dbg!(&property);
}

这是我收到的错误消息:

error[E0515]: cannot return value referencing temporary value
  --> src\main.rs:23:5
   |
23 |     global_object_rcc.borrow_mut().get_property(name)
   |     ------------------------------^^^^^^^^^^^^^^^^^^^
   |     |
   |     returns a value referencing data owned by the current function
   |     temporary value created here

这行不通。 RefCell return 上的 borrow_mut() 是一个 RefMut,它管理可变借用并确保在它存在时没有其他借用。然后调用 get_property 借用 RefMut(隐含地通过 deref 和 &mut self)和 returns 一个引用(&mut Value)与方法的接收者(&mut self)。所以 &mut Value 的生命周期取决于 RefMut 是否还活着;但它在 get_property return 时被销毁,引用无效。

RefCell(就此而言任何 Cell)的全部要点是借用无法“逃脱”。您可以尝试使用 &mut Value 调用的闭包;或者你可以 return 给调用者 RefMut ,缺点是你的类型不能排除调用者保留它,防止将来借用。

调用 .borrow_mut() 时,临时借用的引用被 returned,然后可以取消引用以改变内部数据。您获得的引用将从 borrow_mut() return 秒到 .get_property(name) return 秒存在,这个时间不足以让引用在函数结束后继续存在.

这与Rc<RefCell<Object>>的生命周期不同,它实际上是移入被调用的函数中,并且会在函数returns后被丢弃(但是Rc只会减少refcount,并且只会如果引用计数为 0,则丢弃内部数据)。不能将引用的生命周期 link 编辑为 Rc<> 内的内部数据的生命周期,因为该生命周期直到运行时才知道,基于引用计数。

如果 .borrow_mut() 调用发生在 fn get_property() 之外,并且您将 &mut Object 传递给 fn get_property(),这样它 returns Option<&mut Value,然后您可以使用生命周期变量 link 输入引用生命周期到输出引用生命周期,这将绕过编译器错误:

fn get_property<'a>(global_object_rcc: &'a mut Object, name: &str) -> Option<&'a mut Value> { ... }

但从这个示例的外观来看,这可能不是您想要做的。

制作根据需要改变数据的函数可能会更好,这样您就只有 .borrow_mut() 借用的引用 return 尽可能短的时间(只有很长时间足以改变函数内的数据,以及 return 当您不再需要引用时)。如果您尝试多次借用引用,则长时间保留 .borrow_mut() 引用会导致恐慌!()。