如何解决Rust编译错误cannot borrow `*self` as mutable more than once at a time
错误成因
这是Rust借用规则的正常限制:
- 你遍历
&mut self.map_orderid_position时,已经对self的map_orderid_position字段持有了可变借用,该借用的生命周期覆盖整个for循环体。 - 而
make_order方法要求传入&mut self,也就是对整个结构体的可变借用。Rust的借用检查器目前无法识别「make_order只会修改order_id、不会修改map_orderid_position」这个逻辑,为了避免出现内存安全风险,直接判定两个可变借用冲突,抛出编译错误。
即使这种写法在其他带GC的语言里可以正常运行,Rust为了保证编译期内存安全,也会禁止这类可能存在风险的操作。
修复方案
有两种常用的改法,都可以解决冲突:
方案1:拆分数据收集和订单生成逻辑
先遍历map收集所有需要生成订单的参数,结束对map的借用之后再调用make_order生成订单:
impl Strategy { fn on_depth(&mut self, depth: &Depth) -> Vec<Order> { let mut orders_vec = Vec::new(); // 第一步:不可变借用map,收集所有需要卖出的持仓的量和持仓时长,用完就释放借用 let mut to_sell = Vec::new(); for (_, long_position) in &self.map_orderid_position { let hold_duration = depth.ts - long_position.ts; if hold_duration > 60 * 1000 { to_sell.push((long_position.vol_left, hold_duration)); } } // 第二步:生成订单,此时已经没有对map的借用,调用make_order不会冲突 for (vol, hold_duration) in to_sell { let price = if hold_duration > 90 * 1000 { 0.0 } else { depth.bp_vec[0] }; let order = self.make_order(depth.ts, false, true, vol, price); orders_vec.push(order); } // 注意:你需要补充逻辑,把已经平仓的持仓从map中删除,我这里就不实现具体业务逻辑了 orders_vec } fn make_order(&mut self, ts: u32, is_buy: bool, is_mkt: bool, vol: u32, price: f64) -> Order { let order = Order { id: self.order_id, ts, is_buy, is_mkt, vol, price, }; self.order_id += 1; order } }
方案2:拆分方法,避免借用整个结构体
把make_order改成不需要借用整个self,只传入需要修改的order_id字段,借用检查器可以识别两个不同字段的借用是不冲突的:
impl Strategy { fn on_depth(&mut self, depth: &Depth) -> Vec<Order> { let mut orders_vec = Vec::new(); for (_, long_position) in &mut self.map_orderid_position { let hold_duration = depth.ts - long_position.ts; if hold_duration > 90 * 1000 { // 只传入&mut self.order_id,和map的借用不冲突 let order = Self::make_order(&mut self.order_id, depth.ts, false, true, long_position.vol_left, 0.0); orders_vec.push(order); } else if hold_duration > 60 * 1000 { let order = Self::make_order(&mut self.order_id, depth.ts, false, true, long_position.vol_left, depth.bp_vec[0]); orders_vec.push(order); } } orders_vec } // 改成关联函数,不需要&mut self,只传入需要修改的order_id fn make_order(order_id: &mut u32, ts: u32, is_buy: bool, is_mkt: bool, vol: u32, price: f64) -> Order { let order = Order { id: *order_id, ts, is_buy, is_mkt, vol, price, }; *order_id += 1; order } }
通用规避思路
- 尽量缩短可变借用的生命周期,不要在持有某一个字段的可变借用时,调用需要整个结构体可变借用的方法。
- 如果确定多个操作只会修改结构体的不同字段,可以把方法拆分为只操作对应字段的关联函数,避免借用整个结构体。
- 先收集所有需要的只读数据,释放前一个借用之后再做修改操作,是最常用的规避方式。
内容的提问来源于stack exchange,提问作者YNX
相关产品推荐
相关产品推荐

