为何Rust借用检查器在Arc::get_mut返回None时无法释放引用?
Rust中Arc写时复制(COW)模式的借用检查错误修复
原代码
use std::sync::Arc; struct Foo(Arc<Bar>); #[derive(Clone)] struct Bar; impl Foo { fn bar_mut(&mut self) -> &mut Bar { match Arc::get_mut(&mut self.0) { Some(bar) => bar, None => { let new_bar = Bar::clone(&self.0); self.0 = Arc::new(new_bar); Arc::get_mut(&mut self.0).unwrap() } } } } fn main() { Foo(Arc::new(Bar)).bar_mut(); }
编译错误信息
执行cargo check时出现以下错误:
* Executing task: cargo check Checking testproj v0.1.0 (/home/dspyz/testproj) error[E0502]: 无法将`self.0`不可变借用,因为它已被可变借用 --> src/main.rs:13:42 | 9 | fn bar_mut(&mut self) -> &mut Bar { | - 我们将此引用的生命周期称为`'1` 10 | match Arc::get_mut(&mut self.0) { | - ----------- 在此处发生可变借用 | _________| | | 11 | | Some(bar) => bar, 12 | | None => { 13 | | let new_bar = Bar::clone(&self.0); | | ^^^^^^^ 在此处发生不可变借用 ... | 16 | | } 17 | | } | |_________- 返回此值要求`self.0`被借用至生命周期`'1` error[E0506]: 无法赋值给`self.0`,因为它已被借用 --> src/main.rs:14:17 | 9 | fn bar_mut(&mut self) -> &mut Bar { | - 我们将此引用的生命周期称为`'1` 10 | match Arc::get_mut(&mut self.0) { | - ----------- 在此处借用`self.0` | _________| | | 11 | | Some(bar) => bar, 12 | | None => { 13 | | let new_bar = Bar::clone(&self.0); 14 | | self.0 = Arc::new(new_bar); | | ^^^^^^ 对已借用的`self.0`进行赋值 15 | | Arc::get_mut(&mut self.0).unwrap() 16 | | } 17 | | } | |_________- 返回此值要求`self.0`被借用至生命周期`'1` error[E0499]: 无法同时多次将`self.0`可变借用 --> src/main.rs:15:30 | 9 | fn bar_mut(&mut self) -> &mut Bar { | - 我们将此引用的生命周期称为`'1` 10 | match Arc::get_mut(&mut self.0) { | - ----------- 第一次可变借用发生在此处 | _________| | | 11 | | Some(bar) => bar, 12 | | None => { 13 | | let new_bar = Bar::clone(&self.0); 14 | | self.0 = Arc::new(new_bar); 15 | | Arc::get_mut(&mut self.0).unwrap() | | ^^^^^^^^^^^ 第二次可变借用发生在此处 16 | | } 17 | | } | |_________- 返回此值要求`self.0`被借用至生命周期`'1` Some errors have detailed explanations: E0499, E0502, E0506. For more information about an error, try `rustc --explain E0499`. error: could not compile `testproj` due to 3 previous errors
错误原因
Arc::get_mut(&mut self.0)产生的可变借用会被Rust借用检查器认定为在整个match块中持续有效(直到方法返回引用)。这导致None分支中所有对self.0的操作——不可变借用(克隆)、赋值、再次可变借用——都违反了Rust的借用规则,触发报错。
解决方案
方案1:手动转移所有权打破借用关系
使用std::mem::take将self.0的所有权临时转移,断开原借用关系:
use std::sync::Arc; use std::mem::take; struct Foo(Arc<Bar>); #[derive(Clone)] struct Bar; impl Foo { fn bar_mut(&mut self) -> &mut Bar { let arc = take(&mut self.0); match Arc::get_mut(&mut arc.clone()) { Some(bar) => { self.0 = arc; bar } None => { let new_arc = Arc::new((*arc).clone()); self.0 = new_arc; Arc::get_mut(&mut self.0).unwrap() } } } } fn main() { Foo(Arc::new(Bar)).bar_mut(); }
方案2:使用标准库内置方法(推荐)
Arc::make_mut方法原生实现了“写时复制”逻辑:当Arc是唯一引用时直接返回可变引用,否则克隆内部数据并返回新Arc的可变引用,完全替代手动实现:
use std::sync::Arc; struct Foo(Arc<Bar>); #[derive(Clone)] struct Bar; impl Foo { fn bar_mut(&mut self) -> &mut Bar { Arc::make_mut(&mut self.0) } } fn main() { Foo(Arc::new(Bar)).bar_mut(); }
内容的提问来源于stack exchange,提问作者David Spies
相关产品推荐
相关产品推荐

