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
相关产品推荐
相关产品推荐

