Rust递归转循环编译报错:解决可变与不可变借用冲突
解决Rust循环版本扫描重复字符串的编译错误
问题背景
有一段递归实现的Rust代码,用于扫描文件并报告重复的10字节字符串。尝试将其改写为循环版本时,遇到了借用检查器的编译错误,核心问题是如何在循环中安全地同时向容器添加元素并借用已有元素。
递归实现代码
use std::collections::HashSet; use std::io::Read; use std::fs::File; fn read_strings(hm : HashSet<&str>, fd : &mut File) { let mut buf = [b'a'; 10]; if fd.read_exact(&mut buf).is_err() {return}; let mut hm = hm; let st = std::str::from_utf8(&buf).unwrap(); if hm.contains(&st) { println!("duplicate {}", st); } else { hm.insert(&st); } read_strings(hm, fd) } fn main () { let mut fd = File::open("input.txt").unwrap(); let hm = HashSet::new(); read_strings(hm, &mut fd); }
尝试的循环实现(编译失败)
试图将递归改为循环时编写的代码,因借用冲突无法编译:
use std::collections::HashSet; use std::io::Read; use std::fs::File; fn read_strings(hm : HashSet<&str>, fd : &mut File) { let mut buf = [b'a'; 10]; let mut storage : Vec<String> = vec![]; let mut hm = hm; while fd.read_exact(&mut buf).is_ok() { storage.push(String::from_utf8(buf.to_vec()).unwrap()); let st = storage.last().unwrap().as_str(); if hm.contains(&st) { println!("duplicate {}", st); } else { hm.insert(&st); } } } fn main () { let mut fd = File::open("input.txt").unwrap(); let hm = HashSet::new(); read_strings(hm, &mut fd); }
编译错误信息
$ rustc test.rs error[E0502]: cannot borrow `storage` as mutable because it is also borrowed as immutable --> test.rs:10:9 | 10 | storage.push(String::from_utf8(buf.to_vec()).unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here 11 | let st = storage.last().unwrap().as_str(); | -------------- immutable borrow occurs here 12 | if hm.contains(&st) { | ---------------- immutable borrow later used here error: aborting due to previous error For more information about this error, try `rustc --explain E0502`.
解决方案
方案1:调整变量顺序,避免同作用域的借用冲突
问题出在storage.push(可变借用)和st(来自storage的不可变借用)在同一循环迭代的作用域中同时存在。通过先创建临时字符串,完成HashSet的检查和插入后再将字符串存入storage,可以解决这个冲突:
use std::collections::HashSet; use std::io::Read; use std::fs::File; fn read_strings(hm : HashSet<&str>, fd : &mut File) { let mut buf = [b'a'; 10]; let mut storage : Vec<String> = vec![]; let mut hm = hm; while fd.read_exact(&mut buf).is_ok() { // 先创建临时字符串,获取其引用 let s = String::from_utf8(buf.to_vec()).unwrap(); let st = s.as_str(); // 用引用完成HashSet的检查与插入 if hm.contains(&st) { println!("duplicate {}", st); } else { hm.insert(&st); } // 最后将字符串存入storage,此时st的借用已结束 storage.push(s); } } fn main () { let mut fd = File::open("input.txt").unwrap(); let hm = HashSet::new(); read_strings(hm, &mut fd); }
方案2:直接使用HashSet,避免引用生命周期问题
更简洁的方式是让HashSet直接持有字符串的所有权,而非存储引用,这样完全不需要额外的storage容器,也能避开借用检查的复杂问题:
use std::collections::HashSet; use std::io::Read; use std::fs::File; fn read_strings(mut hm : HashSet<String>, fd : &mut File) { let mut buf = [b'a'; 10]; while fd.read_exact(&mut buf).is_ok() { let s = String::from_utf8(buf.to_vec()).unwrap(); if hm.contains(&s) { println!("duplicate {}", s); } else { hm.insert(s); } } } fn main () { let mut fd = File::open("input.txt").unwrap(); let hm = HashSet::new(); read_strings(hm, &mut fd); }
输入与预期输出
input.txt内容:
aaaaaaaaaabbbbbbbbbbccccccccccaaaaaaaaaadddddddddd
执行后输出:
duplicate aaaaaaaaaa
内容的提问来源于stack exchange,提问作者E. Timotei
相关产品推荐
相关产品推荐

