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

Rust中遍历修改serde_json对象时出现可变借用冲突错误如何解决?

解决Rust serde_json遍历修改时的可变借用冲突

问题代码

use serde_json::{json, Map, Value, Result};


fn hex2value(_hex: &Value) -> Result<Value> {
    Ok(Value::from("converted"))
}

fn walk_object(obj: &mut Map<String, Value>) -> Result<()> {
    for (k, v) in obj.iter_mut() {
        if v.is_object() {
            walk_object(v.as_object_mut().unwrap())?
        } else if v.is_string() {
            obj[k] = hex2value(v)?
        }
    }
    Ok(())
}

fn main() {
    let mut data = json!({
        "name": "John Doe",
        "age": 43,
        "phones": {
            "home": "+44 1234567",
            "work": "+44 2345678"
        }
    });
    walk_object(&mut data.as_object().unwrap());
}

编译错误

error[E0499]: cannot borrow `*obj` as mutable more than once at a time
  --> src/main.rs:13:13
   |
9  |     for (k, v) in obj.iter_mut() {
   |                   --------------
   |                   |
   |                   first mutable borrow occurs here
   |                   first borrow later used here
...
13 |             obj[k] = hex2value(v)?
   |             ^^^ second mutable borrow occurs here

解决方案

方案1:直接修改迭代器中的可变引用

iter_mut()返回的v本身就是对应值的可变引用,直接对v赋值即可,无需二次借用原对象:

use serde_json::{json, Map, Value, Result};


fn hex2value(_hex: &Value) -> Result<Value> {
    Ok(Value::from("converted"))
}

fn walk_object(obj: &mut Map<String, Value>) -> Result<()> {
    for (_k, v) in obj.iter_mut() {
        if v.is_object() {
            walk_object(v.as_object_mut().unwrap())?
        } else if v.is_string() {
            // 直接通过可变引用修改值,避免二次借用obj
            *v = hex2value(v)?;
        }
    }
    Ok(())
}

fn main() {
    let mut data = json!({
        "name": "John Doe",
        "age": 43,
        "phones": {
            "home": "+44 1234567",
            "work": "+44 2345678"
        }
    });
    walk_object(&mut data.as_object().unwrap());
    println!("{:#?}", data);
}

方案2:遍历键的克隆集合

先克隆所有键的独立集合,再遍历克隆后的键操作原对象,避免迭代器持有原对象的可变借用:

use serde_json::{json, Map, Value, Result};


fn hex2value(_hex: &Value) -> Result<Value> {
    Ok(Value::from("converted"))
}

fn walk_object(obj: &mut Map<String, Value>) -> Result<()> {
    // 克隆所有键,生成独立于原obj的键列表
    let keys: Vec<String> = obj.keys().cloned().collect();
    for k in keys {
        let v = obj.get_mut(&k).unwrap();
        if v.is_object() {
            walk_object(v.as_object_mut().unwrap())?
        } else if v.is_string() {
            let new_val = hex2value(v)?;
            obj.insert(k, new_val);
        }
    }
    Ok(())
}

fn main() {
    let mut data = json!({
        "name": "John Doe",
        "age": 43,
        "phones": {
            "home": "+44 1234567",
            "work": "+44 2345678"
        }
    });
    walk_object(&mut data.as_object().unwrap());
    println!("{:#?}", data);
}

原理说明

Rust的借用规则禁止同一时间对同一数据存在多个可变借用。原代码中obj.iter_mut()已获取obj的可变借用,且在整个循环周期内有效;此时通过obj[k]修改值会尝试再次获取obj的可变借用,触发冲突。

方案1直接使用迭代器返回的可变引用修改值,未产生新的可变借用;方案2通过克隆键集合,遍历的是独立列表,原对象的可变借用仅在单次循环中临时存在,不会和迭代器的借用冲突。

内容的提问来源于stack exchange,提问作者DobbyTheElf

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 01:45:34