Rust for循环内借用冲突:cartesian_system_test_2运行失败的解决建议
解决Rust中可变/不可变借用冲突问题
问题背景
两段逻辑一致的测试代码,cartesian_system_test_1()可正常运行,而使用for循环的cartesian_system_test_2()因Rust借用检查机制报错,核心区别仅为手动逐个处理与循环处理的实现方式。
测试源代码
#[cfg(test)] mod cartesian_system_test { use bioshell_cartesians::{CartesianSystem, Coordinates, NbList, PolymerRules}; use bioshell_numerical::Vec3; fn do_modify_vector(vec: &mut Vec3) { vec.x = 0.0; vec.y = 0.0; vec.z = 0.0; } #[test] fn cartesian_system_test_1() { const E_TO: f64 = 6.0; let buffer_thickness = 4.0; let nbl: NbList = NbList::new(E_TO, buffer_thickness, Box::new(PolymerRules {})); let mut coords = Coordinates::new(3);//create a coordinate of length: 5. coords.add(0, 1.0,1.0,1.0); coords.add(1, 2.0,2.0,2.0); coords.add(2, 3.0,3.0,3.0); let mut system: CartesianSystem = CartesianSystem::new(coords, nbl); let mut system_coords = system.coordinates(); assert_eq!(system.coordinates()[0], Vec3::new(1.0, 1.0, 1.0)); assert_eq!(system.coordinates()[1], Vec3::new(2.0, 2.0, 2.0)); assert_eq!(system.coordinates()[2], Vec3::new(3.0, 3.0, 3.0)); let mut vec_0 = system_coords[0].clone(); let mut vec_1 = system_coords[1].clone(); let mut vec_2 = system_coords[2].clone(); do_modify_vector(&mut vec_0); do_modify_vector(&mut vec_1); do_modify_vector(&mut vec_2); system.set_vec(0, vec_0); system.set_vec(1, vec_1); system.set_vec(2, vec_2); assert_eq!(system.coordinates()[0], Vec3::new(0.0, 0.0, 0.0)); assert_eq!(system.coordinates()[1], Vec3::new(0.0, 0.0, 0.0)); assert_eq!(system.coordinates()[2], Vec3::new(0.0, 0.0, 0.0)); } #[test] fn cartesian_system_test_2() { const E_TO: f64 = 6.0; let buffer_thickness = 4.0; let nbl: NbList = NbList::new(E_TO, buffer_thickness, Box::new(PolymerRules {})); let mut coords = Coordinates::new(3); coords.add(0, 1.0,1.0,1.0); coords.add(1, 2.0,2.0,2.0); coords.add(2, 3.0,3.0,3.0); let mut system: CartesianSystem = CartesianSystem::new(coords, nbl); let mut system_coords = system.coordinates(); assert_eq!(system.coordinates()[0], Vec3::new(1.0, 1.0, 1.0)); assert_eq!(system.coordinates()[1], Vec3::new(2.0, 2.0, 2.0)); assert_eq!(system.coordinates()[2], Vec3::new(3.0, 3.0, 3.0)); for i in 0..3{ let mut vec = system_coords[i].clone(); do_modify_vector(&mut vec); system.set_vec(i, vec); } assert_eq!(system.coordinates()[0], Vec3::new(0.0, 0.0, 0.0)); assert_eq!(system.coordinates()[1], Vec3::new(0.0, 0.0, 0.0)); assert_eq!(system.coordinates()[2], Vec3::new(0.0, 0.0, 0.0)); } }
报错信息
error[E0502]: cannot borrow `system` as mutable because it is also borrowed as immutable --> bioshell-cartesians\tests\test_cartesian_system.rs:69:13 | 60 | let mut system_coords = system.coordinates(); | -------------------- immutable borrow occurs here ... 67 | let mut vec = system_coords[i].clone(); | ------------- immutable borrow later used here 68 | do_modify_vector(&mut vec); 69 | system.set_vec(i, vec); | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
解决方案
方案1:移除长期不可变借用,循环内直接获取坐标
问题根源是system_coords持有了system的不可变引用,且在整个循环周期内持续有效,导致无法同时进行可变借用。修改循环部分,直接从system获取坐标克隆,避免持有长期引用:
// 移除提前声明的system_coords变量 // let mut system_coords = system.coordinates(); assert_eq!(system.coordinates()[0], Vec3::new(1.0, 1.0, 1.0)); assert_eq!(system.coordinates()[1], Vec3::new(2.0, 2.0, 2.0)); assert_eq!(system.coordinates()[2], Vec3::new(3.0, 3.0, 3.0)); for i in 0..3{ // 每次循环直接从system获取坐标并克隆,借用仅在当前迭代有效 let mut vec = system.coordinates()[i].clone(); do_modify_vector(&mut vec); system.set_vec(i, vec); }
方案2:提前克隆所有坐标,释放不可变借用
先一次性克隆所有坐标到临时向量,此时system的不可变借用会立即释放,后续循环仅操作临时向量,再批量设置回system:
// 提前克隆所有坐标,完成后system的不可变借用自动释放 let mut coords_clone: Vec<Vec3> = system.coordinates().iter().cloned().collect(); assert_eq!(system.coordinates()[0], Vec3::new(1.0, 1.0, 1.0)); assert_eq!(system.coordinates()[1], Vec3::new(2.0, 2.0, 2.0)); assert_eq!(system.coordinates()[2], Vec3::new(3.0, 3.0, 3.0)); for i in 0..3{ do_modify_vector(&mut coords_clone[i]); system.set_vec(i, coords_clone[i].clone()); }
方案3:修改CartesianSystem提供可变访问接口(若有权限)
如果可以修改CartesianSystem的实现,添加返回可变引用的方法(如coordinates_mut),直接修改内部坐标,无需克隆和调用set_vec:
// 假设CartesianSystem新增如下方法: // fn coordinates_mut(&mut self) -> &mut [Vec3] { // // 返回内部坐标的可变切片 // } // 循环内直接修改 for vec in system.coordinates_mut() { do_modify_vector(vec); }
原因说明
Rust借用检查器禁止同一对象同时存在可变引用和不可变引用。cartesian_system_test_1()中,克隆完所有向量后,system_coords的不可变引用不再被使用,借用检查器允许后续的可变借用;而cartesian_system_test_2()中,system_coords在每次循环迭代都被访问,不可变引用持续存活,此时调用system.set_vec(需要可变引用)就会触发冲突。
内容的提问来源于stack exchange,提问作者user366312
相关产品推荐
相关产品推荐

