如何在Rust中外部可变修改结构体且避免属性拷贝?
问题分析
你遇到的错误是因为Rust的借用规则:s.inners.retain()会对s产生可变借用(要修改Vec),而闭包里的s.max.v又试图对s产生不可变引用。Rust不允许同一时间对同一个值同时存在可变和不可变引用,因此报错。
解决方案
方案1:提前提取max的值(当前场景最优)
既然retain执行过程中max.v不会被修改,直接把它的值提取到局部变量里,闭包使用这个变量就不会再借用s,完全符合Rust规则且无额外开销:
use std::sync::Mutex; #[derive(Debug)] struct Inner { n: i64, factor: i64, } #[derive(Debug)] struct Max { v: i64 } #[derive(Debug)] struct Outer { inners: Vec<Inner>, max: Max, } fn main() { let mutex_s = Mutex::new(Outer { inners: vec![ Inner { n: 1, factor: 2 }, Inner { n: 2, factor: 2 }, Inner { n: 3, factor: 3 }, Inner { n: 4, factor: 3 }, ], max: Max { v: 5 }, }); let mut s = mutex_s.lock().unwrap(); // 提前取出max的值,避免闭包中借用s let max_v = s.max.v; s.inners.retain(|i| i.n * i.factor < max_v); println!("{:?}", s); }
方案2:提前获取max的不可变引用(适合max不可Copy场景)
如果Max中的值不是Copy类型,无法直接复制,可以先获取max的不可变引用。因为Rust允许同时借用结构体的不同字段(只要不是同时可变借用同一个字段),所以这个写法也能通过检查:
// 结构体定义同前 fn main() { let mutex_s = Mutex::new(Outer { /* 初始化同前 */ }); let mut s = mutex_s.lock().unwrap(); // 先拿到max的不可变引用 let max_ref = &s.max; s.inners.retain(|i| i.n * i.factor < max_ref.v); println!("{:?}", s); }
方案3:内部可变性(仅需同时修改max和inners时使用)
如果你的实际场景需要在retain闭包里同时修改max和inners,才需要用RefCell实现内部可变性。但内部可变性会把借用检查从编译期移到运行期,可能引发panic,非必要不建议使用:
use std::sync::Mutex; use std::cell::RefCell; #[derive(Debug)] struct Inner { n: i64, factor: i64, } #[derive(Debug)] struct Max { v: i64 } #[derive(Debug)] struct Outer { inners: Vec<Inner>, max: RefCell<Max>, // 用RefCell包裹实现内部可变性 } fn main() { let mutex_s = Mutex::new(Outer { inners: vec![ Inner { n: 1, factor: 2 }, Inner { n: 2, factor: 2 }, Inner { n: 3, factor: 3 }, Inner { n: 4, factor: 3 }, ], max: RefCell::new(Max { v: 5 }), }); let mut s = mutex_s.lock().unwrap(); s.inners.retain(|i| { let max_v = s.max.borrow().v; i.n * i.factor < max_v }); println!("{:?}", s); }
关于“同时持有可变与不可变引用”
Rust的核心规则是:同一时间,对同一值要么有一个可变引用,要么有任意多个不可变引用,二者不可兼得。但这个规则针对的是同一个值,如果是结构体的不同字段,只要不同时可变借用同一个字段,就可以同时持有不同字段的可变/不可变引用——这也是方案2可行的原因。
内容的提问来源于stack exchange,提问作者Vladimir Alinsky
相关产品推荐
相关产品推荐

