Rust读取CSV遇临时值释放错误:为何绑定变量影响HashMap?
为什么
let binding = fs::read_to_string(...)能解决HashMap的编译错误? 我想要用Rust读取并处理一个CSV格式的文件,文件内容如下:
20 java 30 rust
出现编译错误的代码
fn main() { let file_name = "D:\\download\\data.csv"; let mut counter: HashMap<&str,i32> = HashMap::new(); for line in fs::read_to_string(file_name).unwrap().lines() { let parts: Vec<&str> = line.split("\t").collect(); let count: i32 = parts.get(0).unwrap().trim().parse().unwrap(); let name: &str = name_transformer(parts.get(1).unwrap()); if let Some(time) = counter.get_mut(name) { *time+=count } else { counter.insert(name, count); } }; println!("{:?}",counter); }
编译错误信息
error[E0716]: temporary value dropped while borrowed --> src/main.rs:11:17 | 11 | for line in fs::read_to_string(file_name).unwrap().lines() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use ... 21 | }; | - temporary value is freed at the end of this statement 22 | println!("{:?}",counter); | ------- borrow later used here | help: consider using a `let` binding to create a longer lived value | 11 ~ let binding = fs::read_to_string(file_name).unwrap(); 12 ~ for line in binding.lines() { |
修正后可正常运行的代码
fn main() { let file_name = "D:\\download\\data.csv"; let mut counter: HashMap<&str,i32> = HashMap::new(); let binding = fs::read_to_string(file_name).unwrap(); for line in binding.lines() { let parts: Vec<&str> = line.split("\t").collect(); let count: i32 = parts.get(0).unwrap().trim().parse().unwrap(); let name: &str = name_transformer(parts.get(1).unwrap()); if let Some(time) = counter.get_mut(name) { *time+=count } else { counter.insert(name, count); } }; println!("{:?}",counter); }
疑问解答:为什么添加let binding = ...会影响counter?
这本质是Rust的生命周期规则在起作用——不是binding直接修改counter,而是counter存储的引用依赖于文件内容字符串的生命周期:
错误代码的核心问题:
fs::read_to_string(file_name).unwrap()会创建一个临时的String对象,Rust会在该语句执行完毕(也就是for循环结束后)自动释放这个临时值。- 你从
line.split得到的&str、最终存入counter的name引用,本质都是指向这个临时String内部的内存。 - 当临时
String被释放后,counter里的引用就变成了悬空引用(指向已被回收的内存),这违反了Rust的内存安全准则,因此编译器报错。
修正代码的关键逻辑:
- 把
fs::read_to_string的结果绑定到binding变量后,这个String的生命周期会延续到整个main函数结束(直到println执行完毕后)。 - 所有从
binding.lines()生成的切片引用,生命周期都和binding绑定,存入counter的name引用自然也保持合法。 - 到执行
println!("{:?}",counter)时,binding还未被释放,counter里的引用依然有效,所以编译通过。
- 把
简单总结:原来的临时字符串“死得太早”,导致counter里的引用失效;绑定到变量后,字符串“活的足够久”,能撑到你用完counter的时候。
内容的提问来源于stack exchange,提问作者TongChen
相关产品推荐
相关产品推荐

