【问题标题】:Error Message Unclear: returns a value referencing data owned by the current function错误消息不清楚:返回引用当前函数拥有的数据的值
【发布时间】:2020-12-29 02:52:39
【问题描述】:

我不明白我收到的以下代码的错误消息。

use std::collections::HashMap;
use std::rc::Weak;

struct ThingPool {
    thing_map: HashMap<String, Thing>,
}

impl ThingPool {
    pub fn get(&self, key: &str) -> Option<&Thing> {
        self.thing_map.get(key)
    }
}

struct Thing {
    pool: Weak<RefCell<ThingPool>>,

    key: String,
    parent_key: String,
}

impl Thing {
    pub fn parent(&self) -> Option<&Thing> {
        let pool = &*self
            .pool
            .upgrade()
            .expect("FATAL: ThingPool no longer exists")
            .borrow();

        pool.get(&self.parent_key)
    }
}

fn main() {
    // ...
}

我有一个事物的递归数据结构,我正在尝试编写一个方法来根据其键在 ThingPool 中查找特定事物的父级。我收到的错误消息是:

  --> src/main.rs:30:9
   |
24 |           let pool = &*self
   |  ______________________-
25 | |             .pool
26 | |             .upgrade()
27 | |             .expect("FATAL: ThingPool no longer exists")
28 | |             .borrow();
   | |_____________________- temporary value created here
29 |
30 |           pool.get(&self.parent_key)
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

我知道我不能返回对本地值的引用,但我不明白在这种情况下本地值是什么。什么是“当前函数拥有的数据”?我返回的值是 ThingPool 中 HashMap 的值。我没有复制pool,因此 HashMap 以及该值不应该是函数的本地值。我错过了什么?

【问题讨论】:

    标签: rust


    【解决方案1】:

    当您调用upgrade().expect("...") 时,会创建一个新的Rc,该Rc 归当前函数所有。当您借用Rc 时,该引用不能比您刚刚创建的Rc 寿命长:当Rc 被删除时,它将减少引用计数,可能会释放池并使任何引用无效。目前,当您离开函数时,它会被删除,但函数仍会返回对其内容的引用。

    中断调用链会有所帮助。

    pub fn parent(&self) -> Option<&Thing> {
        let pool_rc = self
            .pool
            .upgrade()
            .expect("FATAL: ThingPool no longer exists");
            
        let pool_ref = pool_rc.borrow();
    
        let parent_ref = pool_ref.get(&self.parent_key);
        
        return parent_ref;
    }
    

    pool_rcRc 指针。它不会被返回,因此它是一个必须在函数调用结束时删除的局部变量。

    pool_ref 是对池的引用。它对引用计数一无所知,它依靠pool_rc 在开始之前增加计数并在结束后减少计数。

    parent_refpool_ref 在同一个泡菜中。它不知道引用计数,并且依赖 pool_rc 一直坚持到 parent_ref 过期后,以便 pool_rcparent_ref 需要访问池时不会减少引用计数。

    当您返回 parent_ref 时,您正试图使其寿命超过 pool_rc,这可能会导致问题。

    下面是一个示例,说明如果允许此功能可能会出错:

    1. 池上有一些外部Rc,所以池上的引用计数为1。
    2. 您运行Thing::parent,返回对池中项目的引用,但由于函数结束时对池的Rc 被删除,池上的引用计数仍为1。
    3. 外部 Rc 被删除,引用计数为 0 并释放池。
    4. 您使用从parent 返回的引用,它引用已释放的内存。

    您也许可以找到一些解决方案,将Rc 与对父级的引用一起返回到池中,以确保它存在。但是,我会提倡一种不同的策略:与其隐藏存在池的事实,不如明确地接受它。将 parent 调用移至 ThingPool 可以清楚地了解所有权动态。

    use std::collections::HashMap;
    
    struct ThingPool {
        thing_map: HashMap<String, Thing>,
    }
    
    impl ThingPool {
        pub fn get(&self, key: &str) -> Option<&Thing> {
            self.thing_map.get(key)
        }
    
        pub fn parent(&self, key: &str) -> Option<&Thing> {
            self.get(key).and_then(|thing| self.get(&thing.parent_key))
        }
    }
    
    struct Thing {
        key: String,
        parent_key: String,
    }
    

    【讨论】:

      猜你喜欢
      • 2022-08-15
      • 1970-01-01
      • 1970-01-01
      • 2019-07-12
      • 1970-01-01
      • 1970-01-01
      • 2020-05-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多