错误[E0597]: 借用的值在 While 循环中存活时间不够长

4

我真的很新手 Rust,我在解决这个错误时遇到了麻烦,但只有当我注释掉 while 语句时才会发生,基本上我正在从控制台请求值并将其存储在 HashMap 中:

use std::collections::HashMap;
use std::io;

fn main() {
    let mut customers = HashMap::new();
    let mut next_customer = true;
    while next_customer {
        let mut input_string = String::new();
        let mut temp_vec = Vec::with_capacity(3);
        let mut vec = Vec::with_capacity(2);
        println!("Insert new customer f.e = customer id,name,address:");
        io::stdin().read_line(&mut input_string);
        input_string = input_string.trim().to_string();
        for s in input_string.split(",") {
            temp_vec.push(s);
        }
        vec.push(temp_vec[1]);
        vec.push(temp_vec[2]);
        let mut key_value = temp_vec[0].parse::<i32>().unwrap();
        customers.insert(key_value, vec);
        next_customer = false;
    }
    println!("DONE");
}

代码出现了错误。
error[E0597]: `input_string` does not live long enough
  --> src/main.rs:14:18
   |
14 |         for s in input_string.split(",") {
   |                  ^^^^^^^^^^^^ borrowed value does not live long enough
...
20 |         customers.insert(key_value, vec);
   |         --------- borrow later used here
21 |         next_customer = false;
22 |     }
   |     - `input_string` dropped here while still borrowed

基本上,vec 最终包含对 input_string 切片的引用,这些切片仅在循环迭代结束时存在,但您尝试将其添加到超出循环生存期的 customers 中。 - Sven Marnach
尝试这样写:input_string.split(",").collect()。更多信息请查看链接:https://users.rust-lang.org/t/solved-string-lifetime-error-borrowed-value-does-not-live-long-enough/5233 - num8er
2
我建议使用 vec.push(temp_vec[1].to_owned()),并对接下来的行进行类似操作。 - Sven Marnach
@sven-marnach 这对我有用,但不确定为什么。 - dreid
@SvenMarnach 怎么样“vec最终包含对input_string切片的引用”,我没有看到它被分配给它或任何东西。 - dreid
trim()split()方法不会分配新的字符串 - 它们返回指向传入的字符串的切片引用,即返回值的类型为&str而不是String。这些切片只在它们所指向的字符串存在期间才存在,也就是在此情况下循环迭代结束之前。 - Sven Marnach
2个回答

5

如其他人所说,问题在于将值放入客户映射中的生命周期和/或类型。

customers.insert(key_value, vec);
   |         --------- borrow later used here

通常这种情况发生在编译器决定给一个对象赋予你意料之外的类型时。为了找出它在做什么,你可以强制指定类型并查看错误信息。将代码改为:

    let mut customers: HashMap<(),()> = HashMap::new();

给我们提供两个相关的错误:
20 |         customers.insert(key_value, vec);
   |                          ^^^^^^^^^ expected `()`, found `i32`
...
20 |         customers.insert(key_value, vec);
   |                                     ^^^ expected `()`, found struct `std::vec::Vec`
   |
   = note: expected unit type `()`
                 found struct `std::vec::Vec<&str>`

所以编译器想要给我们的客户对象提供的类型是HashMap<i32, Vec<&str>>
问题在于,&str生命周期必须在块内部,因为我们不会在任何地方存储String,而且它们不能具有'static生命周期,因为它们是用户输入。
这意味着我们可能想要一个HashMap<i32,Vec<String>>
将代码更改为使用其中一种会导致错误,关于vec没有正确的类型:它被推断为Vec<&str>,但我们想要一个Vec<String>
我们有两个选择:
1. 在将其插入映射之前,将vec转换为正确的类型,使用customers.insert(key_value, vec.iter().map(|s| s.to_string()).collect())。(尽管您可能希望将其提取到变量中以获得更清晰的代码)。 2. 显式更改vec的类型为Vec<String>
选项1“只是起作用”。而选项2则使我们沿着越来越接近read_line调用的相似更改的道路前进。
一旦你决定使用选项1进行修复,你可以删除手动添加的类型注释,如果你觉得它们过于嘈杂。

2
问题在于你传递了对底层 &str 值的引用,这些值将被丢弃。一种方法是获取输入字符串,修剪并拆分它,然后克隆进入另一个向量。
let temp_vec: Vec<String> = input_string.trim().split(",").map(|t| t.to_string()).collect();
vec.push(temp_vec[1].clone());
vec.push(temp_vec[2].clone());

这对我也起作用了,但不确定temp_vec的第一行是如何工作的,我刚接触Rust,我会进行调查和理解的,谢谢。 - dreid
1
基本上,输入字符串被修剪和分割。然后分割有一堆&str切片。我们将这些&str切片转换为完整的字符串。然后将这些字符串收集到向量中。你不能轻易地移动向量中的东西,所以我们只是克隆(创建一个新的字符串副本)并让它进入放置在映射中的新向量中。这样,向量拥有了新克隆的字符串。 - CyanRook

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接