【问题标题】:How to return a reference into a Rc<RefCell<>> function argument? [duplicate]如何将引用返回到 Rc<RefCell<>> 函数参数? [复制]
【发布时间】:2021-03-30 21:04:32
【问题描述】:

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

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

【问题讨论】:

标签: rust borrow-checker


【解决方案1】:

这行不通。 RefCell 上的 borrow_mut() 返回一个 RefMut,它管理可变借用并确保在它还活着的时候没有其他借用。对get_property 的调用然后借用RefMut(隐式通过deref 和&amp;mut self)并返回与方法的接收者(&amp;mut self)具有相同生命周期的引用(&amp;mut Value)。所以&amp;mut Value 的生命周期取决于RefMut 是否还活着;但是当get_property 返回时它被破坏,使引用无效。

RefCell(任何Cell)的全部意义在于借用不能“逃脱”。您可以尝试使用&amp;mut Value 调用的闭包;或者你可以将RefMut返回给调用者,缺点是你的类型不能排除调用者保留它,防止将来借用。

【讨论】:

  • 当然,呵呵。如果我可以将 &mut 返回到一个比它寿命更长的 RefMut 中,那将违背整个目的。
  • 我需要记住的是,部分可变借用仍然是可变借用。
【解决方案2】:

调用.borrow_mut() 时,会返回一个临时借用的引用,然后可以取消引用以改变内部数据。您获得的引用将从borrow_mut() 返回到.get_property(name) 返回时有效,这不足以让引用在函数结束后有效。

这和Rc&lt;RefCell&lt;Object&gt;&gt;的生命周期不同,它实际上是移到被调用的函数中,一旦函数返回就会被丢弃(但是Rc只会递减refcount,只有当refcount为0时才会丢弃内部数据)。引用的生命周期不能与Rc&lt;&gt; 内的内部数据的生命周期相关联,因为根据引用计数,直到运行时才知道该生命周期。

如果.borrow_mut() 调用发生在fn get_property() 之外,并且您将&amp;mut Object 传递给fn get_property(),使其返回Option&lt;&amp;mut Value,那么您可以使用生命周期变量来链接输入引用生命周期到输出引用生命周期,这将绕过编译器错误:

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

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

根据需要创建改变数据的函数可能会更好,这样你就只有.borrow_mut()返回的借用引用尽可能短的时间(只要足够长的时间来改变函数内的数据,并在您不再需要参考时返回)。持有 .borrow_mut() 引用的时间过长可能会导致恐慌!() 如果您尝试多次借用引用。

【讨论】:

    猜你喜欢
    • 2023-01-20
    • 1970-01-01
    • 2021-04-06
    • 1970-01-01
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    • 2015-07-28
    • 1970-01-01
    相关资源
    最近更新 更多