为何直接用Vec迭代器无报错,调用方法返回的迭代器触发Rust借用检查错误?
Rust借用检查器:直接创建迭代器vs封装方法的差异
先看这段Rust代码:
struct Foo { bar: Vec<i32>, baz: i32 } impl Foo { fn get_an_iter(&self) -> impl Iterator<Item=&i32> { self.bar.iter() } fn do_mut_stuff_works(&mut self) { for num in self.bar.iter() { self.baz += num; } } fn do_mut_stuff_does_not_work(&mut self) { for num in self.get_an_iter() { self.baz += num; } } }
问题描述
do_mut_stuff_works方法里,直接遍历self.bar的迭代器并修改self.baz能正常编译;但把创建迭代器的逻辑封装到get_an_iter方法后,do_mut_stuff_does_not_work调用该方法遍历就会触发借用检查器报错。
编译错误信息如下:
Compiling playground v0.0.1 (/playground) error[E0502]: cannot borrow `self.baz` as mutable because it is also borrowed as immutable --> src/lib.rs:20:13 | 19 | for num in self.get_an_iter() { | ------------------ | | | immutable borrow occurs here | immutable borrow later used here 20 | self.baz += num; | ^^^^^^^^^^^^^^^ mutable borrow occurs here For more information about this error, try `rustc --explain E0502`. error: could not compile `playground` (lib) due to 1 previous error
原因分析
核心差异在于借用检查器的分析范围:
- 直接调用
self.bar.iter()时,借用检查器能精准识别:迭代器仅借用了Foo的bar字段,属于单个字段的不可变借用。而修改self.baz是对另一个独立字段的可变借用,两者无重叠,符合Rust"不同字段的借用可共存"的规则。 - 调用
get_an_iter(&self)时,方法签名里的&self表示对整个Foo实例的不可变借用。尽管方法内部只用到了bar字段,但借用检查器不会穿透方法调用分析内部实现——它只会依据签名判断:整个self已被不可变借用,此时再尝试可变借用self.baz,就违反了"同一实例不能同时存在可变与不可变借用"的规则。
解决方法
缩小方法的借用范围,只针对bar字段而非整个self即可:
struct Foo { bar: Vec<i32>, baz: i32 } impl Foo { // 改为接收bar字段的引用,而非整个self fn get_an_iter(bar: &Vec<i32>) -> impl Iterator<Item=&i32> { bar.iter() } fn do_mut_stuff_works(&mut self) { for num in self.bar.iter() { self.baz += num; } } fn do_mut_stuff_now_works(&mut self) { // 调用时传入bar字段的引用 for num in Self::get_an_iter(&self.bar) { self.baz += num; } } }
修改后,借用检查器能识别迭代器仅借用bar字段,修改baz是独立的可变借用,两者可共存,编译即可通过。
内容的提问来源于stack exchange,提问作者cadolphs
相关产品推荐
相关产品推荐

