You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 06:42:34