Rust中如何规避方法调用时的两次可变借用报错
报错根本原因
Rust的借用检查基于静态作用域规则做校验,不会根据运行时的Option变体状态动态调整借用生命周期:
- 第一次调用
self.find_command_by_name(input)返回Option<&mut Command>时,就已经对self发起了可变借用,这个借用的生命周期和返回值绑定 - 只要存储返回值的
cmd变量还在当前作用域内,无论它实际是Some还是None,借用检查器都会判定self处于被可变借用占用的状态 - 原代码中
if cmd.is_some() { return cmd }的逻辑虽然在运行时拿到Some就会提前返回,但在走None的分支时,cmd变量依然存活,第一次借用没有被释放,此时再次申请&mut self调用find_command_by_alias就会触发重复可变借用错误。
可直接使用的修复方案
方案1:用match做分支隔离(最直观)
match的分支天然具备作用域互斥性,借用检查器可以识别到None分支下不存在第一次调用返回的可变引用,第一次借用已经结束:
fn find_command(&mut self, input: &Vec<String>) -> Option<&mut Command> { match self.find_command_by_name(input) { Some(cmd) => Some(cmd), None => self.find_command_by_alias(input), } }
方案2:用Option组合子链式调用(最简洁)
or_else是惰性求值的组合子,只会在前序返回None时才执行传入的闭包,闭包和前序调用的借用作用域完全隔离,不会产生借用冲突:
fn find_command(&mut self, input: &Vec<String>) -> Option<&mut Command> { self.find_command_by_name(input) .or_else(|| self.find_command_by_alias(input)) }
注意:不要使用非惰性的
or方法,它会在调用组合子前就求值第二次调用的逻辑,依然会触发重复借用错误。
此类场景的通用设计参考
- 优先使用
Option/Result标准组合子(or_else、and_then、unwrap_or_else)串联存在互斥借用的调用,这类API的签名设计天然适配借用规则,不会产生生命周期冲突 - 涉及借用返回值的分支判断优先用
match,不要先把返回值赋值给中间变量再用is_some()/is_none()判断,match的分支作用域隔离能让借用检查器更精准地识别借用的起止点 - 尽可能缩短持有
self借用的变量的存活作用域,能链式调用就不要提前声明中间变量,减少不必要的借用占用 - 只有在能100%证明内存安全、且静态借用规则确实无法表达逻辑时,才考虑用
RefCell等内部可变性容器把借用检查挪到运行期,不要把内部可变性作为规避借用规则的常规方案。
内容的提问来源于stack exchange,提问作者Janos
相关产品推荐
相关产品推荐

