我对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
会转让所有权,因此记录也可以在循环之外使用。我在这里做错了什么?
答案 0 :(得分: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
,其中包含对某些字符串的引用,但这些字符串的所有者是什么?它是rr
;因此你的错误信息。
遵循代码:
Reader::from_path
从磁盘读取CSV,rdr
拥有该结果。
Reader::records
的文档说(强调我的):
将所有记录中的借来的迭代器作为字符串返回。
因此迭代器不能超过Reader
。
StringRecord::get
的API是:
pub fn get(&self, i: usize) -> Option<&str>
这将返回一个字符串引用,该引用仅与self
一样长。
通过对此进行跟踪,您尝试插入的字符串切片实际上是StringRecord
拥有的引用数据。这些记录将在for
循环体的末尾删除,如错误消息中所示。它会导致内存不安全,允许你在循环后拥有这些引用,因此编译器会阻止你。
插入String
代替允许代码继续:
topics.insert((filename, value1.to_owned()), value2.to_owned());
我想插入
HashMap
转让所有权
是的,确实如此。转让所有权的参考。那些参考文献所指的不是。
另见:
答案 1 :(得分:0)
问题是只有CSV行存在时,CSV字段才会生效,并且您尝试将字段借用保存到比CSV读取器更长的主题中。
为此,您需要HashMap
通过复制或移动字段到hashmap来获取CSV字段的所有权。为此,您需要在地图中使用String
而不是&str
s。
这将做你想要的:
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());
}
}
}