rust “借来的价值活得不够长”

cetgtptt  于 2023-08-05  发布在  其他
关注(0)|答案(2)|浏览(105)

我有以下代码:

use std::time::SystemTime;
use std::collections::HashMap;
use serde_json;

fn main()
{
    let mut payload = HashMap::<&String, u64>::new();
    match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
        Ok(n) => {
            let timestamp = "timestamp".to_string();
            payload.insert(&timestamp, n.as_secs());
        }
        Err(_) => panic!("SystemTime before UNIX EPOCH!"),
    };
    println!("{}", serde_json::to_string(&payload).unwrap())
}

字符串
编译器会在以下情况下福尔斯:

error[E0597]: `timestamp` does not live long enough
  --> src/main.rs:11:28
   |
10 |             let timestamp = "timestamp".to_string();
   |                 --------- binding `timestamp` declared here
11 |             payload.insert(&timestamp, n.as_secs());
   |                            ^^^^^^^^^^ borrowed value does not live long enough
12 |         }
   |         - `timestamp` dropped here while still borrowed
...
15 |     println!("{}", serde_json::to_string(&payload).unwrap())
   |                                          -------- borrow later used here


我可以通过将let timestamp = "timestamp".to_string();移到match之上来解决这个问题,但我不禁觉得还有更好的解决方案,而且我确实不明白为什么编译器没有对n提出同样的抱怨。
有没有一个解决方案,包括将timestamp的定义保留在Ok子句中?

6psbrbz9

6psbrbz91#

这里最大的问题是这两行:

{
    let timestamp = "timestamp".to_string();
    payload.insert(&timestamp, n.as_secs());
}

字符串
这就是所谓的 * 对时态变量的引用 *。由于HashMap中的键是&String引用,因此键的实际数据并不存储在HashMap中。他们必须住在某个地方。
在您的代码中,键的实际数据所在的位置是timestamp变量。但是在insert() ing之后,您离开了timestamp变量所在的作用域,它被销毁了;但是payload中的条目仍然存在,并且该键成为悬空引用。编译器不允许这样做,因此出现错误。
有两种方法可以解决此问题:

  • 将密钥存储在HashMap中,方法是将密钥设置为String
  • 确保键的寿命超过payload对象

将密钥存储在HashMap

这可能看起来像这样:

use serde_json;
use std::collections::HashMap;
use std::time::SystemTime;

fn main() {
    let mut payload = HashMap::<String, u64>::new();
    match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
        Ok(n) => {
            let timestamp = "timestamp".to_string();
            payload.insert(timestamp, n.as_secs());
        }
        Err(_) => panic!("SystemTime before UNIX EPOCH!"),
    };
    println!("{}", serde_json::to_string(&payload).unwrap())
}
{"timestamp":1690640840}

的数据

确保密钥的寿命比HashMap

use serde_json;
use std::collections::HashMap;
use std::time::SystemTime;

fn main() {
    let mut payload = HashMap::<&str, u64>::new();
    match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
        Ok(n) => {
            let timestamp = "timestamp";
            payload.insert(timestamp, n.as_secs());
        }
        Err(_) => panic!("SystemTime before UNIX EPOCH!"),
    };
    println!("{}", serde_json::to_string(&payload).unwrap())
}
{"timestamp":1690640900}

第二个例子有效的原因是,如果你不对字符串调用to_string(),它将是一个存储在全局常量内存中的&'static str,并且永远存在。
请注意,这个版本现在不再与String s兼容,它只能存储&str引用,这些引用的寿命肯定超过payload对象。使用String s作为键有点浪费,但比使用&str灵活得多。

tf7tbtn2

tf7tbtn22#

n不是借来的。timestamp被借用,并且借用放入容器中,然后timestamp超出范围(这使容器无效)。一个装思想的容器而不是借来的怎么样。
例如let mut payload = HashMap::<String, u64>::new();。你将不得不修复另一个借用时间戳的地方。我把这个留给你。

相关问题