Rust中可变借用离开作用域后为何仍无法进行不可变借用?
你的理解疏漏点
你对借用规则的认知没有错,但搞错了闭包持有捕获变量借用的生命周期范围:
闭包对环境中变量的借用,是从闭包被创建的那一刻开始持有,一直持续到闭包自身被销毁(离开作用域)、或者最后一次被使用的位置为止,不是仅在闭包被调用的短暂时间内持有借用。
对应到你的代码里:
- 你创建
inspect闭包时,它就已经拿到了ball的不可变借用。因为你在代码最后还要调用一次inspect,所以这个不可变借用的活跃期会从inspect创建的位置一直持续到最后一次inspect调用结束,完整覆盖了后面edit闭包的创建和调用区间。 - 你在内部作用域创建
edit闭包时,它需要获取ball的可变借用,此时inspect持有的不可变借用还处于活跃状态,直接触发了借用检查错误:同一作用域内不能同时存在活跃的可变借用和不可变借用。 - 你原本预期
edit离开内部作用域就释放可变借用,这个判断本身是对的,但错误在edit创建的那一刻就已经触发了,和edit后续什么时候销毁没有关系。 - 这也能解释为什么注释掉最后一行
inspect()就能编译通过:此时inspect的最后一次使用就是第一次调用的位置,它持有的不可变借用在第一次调用结束后就被释放,后续创建edit时没有活跃的不可变借用冲突,自然可以正常编译。
可行修复方案
核心思路就是让两个闭包持有的借用区间完全不重叠,或者避免闭包长期持有借用:
- 方案1:手动缩小闭包作用域,让
inspect在edit创建前就销毁,需要再次调用时重新创建闭包
#[derive(Default, Debug)] pub struct Ball { pub color: String, pub size: String, } pub fn moves() { let mut ball = Ball { color: "blue".to_string(), size: "small".to_string(), }; { let inspect = || { println!("Unremarkable. {} ball. ", (&ball).color); }; inspect(); } // 此处inspect离开作用域,持有的不可变借用完全释放 { let mut edit = || { ball.color = "red".to_string(); }; edit() }; // edit离开作用域,可变借用释放 // 需要再次检查时重新创建inspect闭包 let inspect = || { println!("Unremarkable. {} ball. ", (&ball).color); }; inspect(); }
- 方案2:不让闭包直接捕获环境中的
ball,改为通过参数传入引用,这样借用只会在闭包调用时临时产生,调用结束立即释放,不会长期占用
#[derive(Default, Debug)] pub struct Ball { pub color: String, pub size: String, } pub fn moves() { let mut ball = Ball { color: "blue".to_string(), size: "small".to_string(), }; // 闭包通过参数接收引用,不长期捕获环境变量 let inspect = |ball: &Ball| { println!("Unremarkable. {} ball. ", ball.color); }; let mut edit = |ball: &mut Ball| { ball.color = "red".to_string(); }; inspect(&ball); // 临时产生不可变借用,调用结束立即释放 edit(&mut ball); // 临时产生可变借用,调用结束立即释放 inspect(&ball); // 此时无活跃借用,可正常调用 }
内容的提问来源于stack exchange,提问作者rtviii
相关产品推荐
相关产品推荐

