1

我是 Rust 整个生命周期概念的新手。我正在尝试从 CSV 文件中读取一些数据并将它们放入HashMap

extern crate csv;

use std::collections::HashMap;

fn main() {
    let files = vec!["file1.csv", "file2.csv", "file3.csv"];

    let mut topics: HashMap<(&str, &str), &str> = HashMap::new();
    for filename in files {
        let mut rdr = csv::Reader::from_path(filename).unwrap();

        for rec in rdr.records() {
            let rr = rec.unwrap();
            let value1 = rr.get(0).unwrap();
            let value2 = rr.get(1).unwrap();
            topics.insert((filename, value1), value2);
        }
    }
}

游乐场链接

但是会出现以下错误:

error[E0597]: `rr` does not live long enough
  --> src/main.rs:14:26
   |
14 |             let value1 = rr.get(0).unwrap();
   |                          ^^ borrowed value does not live long enough
...
17 |         }
   |         - `rr` dropped here while still borrowed
18 |     }
19 | }
   | - borrowed value needs to live until here

我认为插入HashMap转移所有权,因此记录也可以在循环之外使用。我在这里做错了什么?

4

2 回答 2

2

看这段代码:

let mut topics: HashMap<(&str, &str), &str> = HashMap::new();
for filename in files {
    let mut rdr = csv::Reader::from_path(filename).unwrap();

    for rec in rdr.records() {
        let rr = rec.unwrap();
        let value1 = rr.get(0).unwrap();
        let value2 = rr.get(1).unwrap();
        topics.insert((filename, value1), value2);
    }
}

它创建一个HashMap包含对某些字符串的引用的 a,但是这些字符串的所有者是什么?它是rr;因此你的错误信息。

按照代码:

  1. Reader::from_path从磁盘读取 CSV 并rdr拥有该结果。

  2. 文档Reader::records说(强调我的):

    将所有记录的借用迭代器作为字符串返回。

    因此迭代器不能比Reader.

  3. StringRecord::get的 API 是:

    pub fn get(&self, i: usize) -> Option<&str>
    

    这将返回一个字符串引用,它的寿命与它一样长self

通过跟踪这一点,您尝试插入的字符串切片实际上引用了StringRecord. 这些记录被丢弃在for循环体的末尾,如错误消息中所示。允许您在循环之后拥有这些引用会导致内存不安全,因此编译器会阻止您。

插入Strings 反而允许代码继续:

topics.insert((filename, value1.to_owned()), value2.to_owned());

我认为插入HashMap转让所有权

是的,它确实。引用的所有权被转移。这些参考文献所指的不是。

也可以看看:

于 2018-05-18T21:19:53.533 回答
0

问题是 CSV 字段仅在 CSV 行存在时才存在,并且您试图将字段的借用保存到比 CSV 阅读器寿命更长的主题中。

为此,您需要HashMap通过将字段复制或移动到哈希图中来获得 CSV 字段的所有权。为此,您需要String在地图中使用 s 而不是&strs。

这将做你想要的:

extern crate csv;

use std::collections::HashMap;

fn main() {
    let files = vec!["file1.csv", "file2.csv", "file3.csv"];

    let mut topics: HashMap<(String, String), String> = HashMap::new();
    for filename in files {
        let mut rdr = csv::Reader::from_path(filename).unwrap();

        for rec in rdr.records() {
            let rr = rec.unwrap();
            let value1 = rr.get(0).unwrap();
            let value2 = rr.get(1).unwrap();
            topics.insert((filename.into(), value1.into()), value2.into());
        }
    }
}
于 2018-05-19T10:47:04.550 回答