Rust简单应用中的多重借阅错误及合规代码设计咨询
Rust HashMap 修改与删除的借用冲突解决
我正在通过开发小程序学习Rust,但学习曲线比预期陡峭。我编写了一段操作HashMap的代码:该HashMap存储自定义Custom结构体对象,需求是按条件修改部分值并删除部分条目,但遭遇借用编译器错误。
最初代码在同一循环中对HashMap进行可变引用迭代,同时收集待删除键,后续删除时出现两次可变借用冲突;将修改和删除拆分到独立函数后,又出现不可变借用与可变借用的生命周期冲突,即使将不可变迭代放入独立块也无法解决。我理解Rust的引用规则,但希望了解如何遵循Rust引用系统来正确设计这段代码,而非采用临时变通方案。
初始错误代码
use std::collections::HashMap; // 定义Custom结构体 pub struct Custom { x: u8, } fn main() { // 创建存储String键和Custom对象的HashMap let mut custom_map: HashMap<String, Custom> = HashMap::new(); // 插入测试数据 custom_map.insert("apple".to_string(), Custom { x: 10 }); custom_map.insert("banana".to_string(), Custom { x: 20 }); custom_map.insert("grape".to_string(), Custom { x: 30 }); custom_map.insert("quince".to_string(), Custom { x: 40 }); // 包含'q'的测试条目 let mut elements_to_be_deleted: Vec<&String> = Vec::new(); // 迭代HashMap的键和值 for (key, custom) in &mut custom_map { if key.contains('a') { // 对包含'a'的键对应的x值加1 custom.x += 1; } if key.contains('q') { // 收集需要删除的键 elements_to_be_deleted.push(key); } } // 删除标记的条目 for key in &elements_to_be_deleted { custom_map.remove(*key); } // 打印更新后的值 for (key, custom) in &custom_map { println!("Key: {}, x: {}", key, custom.x); } }
初始错误信息
| 21 | for (key, custom) in &mut custom_map { | --------------- 第一次可变借用发生在此处 34 | for key in &elements_to_be_deleted { | ----------------------- 第一次借用在此处被后续使用 35 | custom_map.remove(*key); | ^^^^^^^^^^ 第二次可变借用发生在此处
修改后错误代码
use std::collections::HashMap; // 定义Custom结构体 pub struct Custom { x: u8, } fn main() { // 创建存储String键和Custom对象的HashMap let mut custom_map: HashMap<String, Custom> = HashMap::new(); // 插入测试数据 custom_map.insert("apple".to_string(), Custom { x: 10 }); custom_map.insert("banana".to_string(), Custom { x: 20 }); custom_map.insert("grape".to_string(), Custom { x: 30 }); custom_map.insert("quince".to_string(), Custom { x: 40 }); // 包含'q'的测试条目 let mut elements_to_be_deleted: Vec<&String> = Vec::new(); // 迭代HashMap的键和值 for (key, custom) in &custom_map { if key.contains('q') { // 收集需要删除的键 elements_to_be_deleted.push(&key); } } // 修改HashMap条目 modifyMapElements(&mut custom_map); // 删除HashMap条目 deleteElementsFromMap(&mut custom_map, elements_to_be_deleted); // 打印更新后的值 for (key, custom) in &custom_map { println!("Key: {}, x: {}", key, custom.x); } } fn deleteElementsFromMap(map: &mut HashMap<String, Custom>, els_to_delete: Vec<&String>) { for key in els_to_delete { map.remove(&key); } } fn modifyMapElements(map: &mut HashMap<String, Custom>) { for (key, custom) in map { if key.contains('a') { // 对包含'a'的键对应的x值加1 custom.x += 1; } } }
修改后错误信息
for (key, custom) in &custom_map { | ----------- 不可变借用发生在此处 ... 32 | deleteElementsFromMap(&mut custom_map, elements_to_be_deleted); | ^^^^^^^^^^^^^^^ ---------------------- 不可变借用在此处被后续使用 | | | 可变借用发生在此处
正确解决方案
方案1:收集键的副本,分离操作步骤
核心思路是避免持有HashMap的引用跨多个可变操作,通过收集待删除键的所有权副本(而非引用),让后续的修改和删除操作不再依赖之前的借用。
use std::collections::HashMap; pub struct Custom { x: u8, } fn main() { let mut custom_map: HashMap<String, Custom> = HashMap::new(); custom_map.insert("apple".to_string(), Custom { x: 10 }); custom_map.insert("banana".to_string(), Custom { x: 20 }); custom_map.insert("grape".to_string(), Custom { x: 30 }); custom_map.insert("quince".to_string(), Custom { x: 40 }); // 第一步:收集待删除键的副本(获取所有权,不保留HashMap引用) let elements_to_be_deleted: Vec<String> = custom_map.keys() .filter(|key| key.contains('q')) .cloned() .collect(); // 第二步:修改需要更新的条目(仅持有当前可变引用) for (key, custom) in custom_map.iter_mut() { if key.contains('a') { custom.x += 1; } } // 第三步:删除条目(无之前的引用依赖) for key in elements_to_be_deleted { custom_map.remove(&key); } // 打印结果 for (key, custom) in &custom_map { println!("Key: {}, x: {}", key, custom.x); } }
为什么可行:
cloned()将HashMap中键的引用转换为String的副本,elements_to_be_deleted完全持有这些字符串的所有权,不再依赖原HashMap的任何引用。- 三个步骤依次执行,每个步骤仅持有HashMap的单一类型引用(不可变/可变),且步骤完成后引用立即释放,完全符合Rust的借用规则。
方案2:使用HashMap内置的retain方法(更简洁)
Rust标准库为HashMap提供了retain方法,它可以在一次迭代中同时完成条件修改和条件删除,内部由标准库保证符合借用规则,无需手动管理引用。
use std::collections::HashMap; pub struct Custom { x: u8, } fn main() { let mut custom_map: HashMap<String, Custom> = HashMap::new(); custom_map.insert("apple".to_string(), Custom { x: 10 }); custom_map.insert("banana".to_string(), Custom { x: 20 }); custom_map.insert("grape".to_string(), Custom { x: 30 }); custom_map.insert("quince".to_string(), Custom { x: 40 }); // 使用retain:返回true保留条目,false删除;同时可修改条目 custom_map.retain(|key, custom| { // 先处理值的修改 if key.contains('a') { custom.x += 1; } // 返回是否保留条目:不包含'q'则保留 !key.contains('q') }); // 打印结果 for (key, custom) in &custom_map { println!("Key: {}, x: {}", key, custom.x); } }
为什么可行:
retain方法是HashMap的原生方法,它在迭代过程中安全地处理了条目修改和删除,避免了手动操作时的引用交叉问题,同时代码更简洁。
内容的提问来源于stack exchange,提问作者theAsker
相关产品推荐
相关产品推荐

