使用 HashMap 引用作为值的 HashMap 的生命周期问题

问题描述

(Rust 新手,仅供参考!)

所以 - 我一直试图在 Rust 中理解生命周期的概念。我已经阅读了文档,并阅读了一些关于该主题博客和 SO 帖子。但仍然没有完全明白(因此,这个问题的标题可能不好)。

我有一个特别的问题,我想弄清楚,我已经将其归结为这个小示例代码(试图尽可能接近工作示例):

use std::collections::HashMap;

// Imagine this is actually a more complex type:
type BigComplexType = i32;

fn some_expensive_computation() -> BigComplexType {
    // Imagine this takes some arguments,and takes a long time to run
    return 123;
}

fn construct() -> HashMap<i32,&'static HashMap<i32,BigComplexType>> {
    let mut main = HashMap::new();

    let mut nested = HashMap::new();
    nested.insert(1111,some_expensive_computation());
    nested.insert(2222,some_expensive_computation());
    // ... and lots more inserts...

    main.insert(10,&nested);
    main.insert(20,&nested);
    // ... and lots more inserts...

    // Imagine a lot more other nested HashMaps to follow here
    // let mut nested2 = ...
    // ...
    // main.insert(...,&nested2);
    // ...

    return main;
}


fn main() {
    construct();
}

这个例子有点琐碎 - 在实际代码中,我在 construct() 函数中创建了更复杂和更深层次的结构。

我想要做的是创建某种缓存来保存这些预先计算的值,以便可以在代码中的其他地方轻松快速地访问它们。也许这一切都可以以某种完全不同的方式完成 - 但我认为必须有一种方法可以做到这一点。

但是,rustc在这里抱怨,因为nested只存在于construct()中,一旦我们退出函数,它就不存在了,因此所有的引用都是无效的(或者,这就是我对问题的理解)。

我尝试为 'a 函数引入 construct() 生命周期,并在下面的 nested HashMap 上使用该生命周期,但没有骰子。遇到了一些错误,并且永远无法使其完全发挥作用。我尝试了各种添加生命周期注释的变体,但没有骰子。

我有一种感觉,我只是没有在这里理解整个概念的某些方面。谁能帮我指明正确的方向?

(我确实考虑过将 nested HashMap 收集到一个向量中并沿着 main 哈希图返回 - 这样该函数就会返回主 HashMap 以及嵌套的向量 -因此,寿命将得到保证,我认为 - 但我在尝试时遇到了其他一些障碍,并且感觉我把事情复杂化了。)

作为参考,这是我在编译上述内容时遇到的错误

error[E0515]: cannot return value referencing local variable `nested`
  --> lifetime_return.rs:29:12
   |
19 |     main.insert(10,&nested);
   |                     ------- `nested` is borrowed here
...
29 |     return main;
   |            ^^^^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing local variable `nested`
  --> lifetime_return.rs:29:12
   |
20 |     main.insert(20,&nested);
   |                     ------- `nested` is borrowed here
...
29 |     return main;
   |            ^^^^ returns a value referencing data owned by the current function

error: aborting due to 2 prevIoUs errors

For more information about this error,try `rustc --explain E0515`.

任何/所有帮助将不胜感激!我试图寻找类似的问题,但找不到一个 - 也许我只是没有将某些 SO 帖子识别为重复,因为我还没有完全理解生命周期模型。

解决方法

您对为什么这不起作用的直觉是正确的:nested 只存在于 construct 内,并且您尝试在哈希图中返回对它的引用,该引用的存在时间比函数长。假设您不想克隆嵌套映射,大概是因为它们非常大,您可以使用 Rc 代替作为对嵌套映射进行简单可克隆的引用的一种方式,这些引用使它们保持活动状态必要的:

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

type BigComplexType = i32;

fn some_expensive_computation() -> BigComplexType {
    return 123;
}

fn construct() -> HashMap<i32,Rc<HashMap<i32,BigComplexType>>> {
    let mut main = HashMap::new();

    let mut nested = HashMap::new();
    nested.insert(1111,some_expensive_computation());
    nested.insert(2222,some_expensive_computation());

    let nested_rc = Rc::new(nested);
    main.insert(10,Rc::clone(&nested_rc));
    main.insert(20,nested_rc); // can move the Rc for the last insert
    
    let mut nested2 = HashMap::new();
    nested2.insert(3333,some_expensive_computation());
    nested2.insert(4444,some_expensive_computation());
    
    let nested2_rc = Rc::new(nested2);
    main.insert(30,Rc::clone(&nested2_rc));
    main.insert(40,nested2_rc);

    return main;
}


fn main() {
    let map = construct();
    println!("{}",map[&10][&1111]); // 123
}

Playground link