Rust中into_iter的可变借用为何持续限制后续变量访问?
问题描述
#[derive(Clone, Copy)] struct Data<'a> { i: &'a [i32], start: i32, } impl<'a> Data<'a> { fn new<const I: usize>(arr: &[i32; I]) -> Data { Data { i: arr, start: 0 } } } impl<'a> Iterator for Data<'a> { type Item = &'a i32; fn next(&mut self) -> Option<Self::Item> { if (self.start as usize) < self.i.len(){ let r = &self.i[self.start as usize]; self.start+=1; Some(r) }else{ None } } } struct WrapperData<'a> { v: &'a Data<'a>, } impl<'a> IntoIterator for &'a mut WrapperData<'a> { type Item = &'a i32; type IntoIter = Data<'a>; fn into_iter(self) -> Self::IntoIter { *self.v } } fn main(){ let arr = [1, 2, 3]; let d = Data{start:0,i:&arr}; let mut wr = WrapperData { v: &d }; let ddd = wr.into_iter(); // #1 let r = wr.v; // #2 error }
在#1处调用wr.into_iter()时会创建对wr的可变借用,但直观上认为方法调用结束后该可变借用已不再被使用,然而编译器仍提示#2处访问wr.v存在错误,报错信息如下:
69 | let r = wr.v; | ^^^^ | | | use of borrowed `wr` | borrow later used here
问题原因解析
核心问题出在生命周期的强制绑定上:
WrapperData<'a>的定义中,v是&'a Data<'a>,这意味着v的引用生命周期和Data内部切片的生命周期被强制绑定为同一个'a。- 当为
&'a mut WrapperData<'a>实现IntoIterator时,返回的Data<'a>会继承这个绑定关系,Rust的借用检查器会认为,返回的ddd(Data<'a>)和原始wr共享同一个生命周期范围。 - 这就导致
wr的可变借用并没有在into_iter调用结束后释放,而是被延长到了ddd存在的整个周期,后续访问wr.v自然会触发可变借用和不可变借用的冲突。
修复方案
通过拆分WrapperData的生命周期参数,打破引用和内部数据的生命周期绑定即可解决:
#[derive(Clone, Copy)] struct Data<'a> { i: &'a [i32], start: i32, } impl<'a> Data<'a> { fn new<const I: usize>(arr: &[i32; I]) -> Data { Data { i: arr, start: 0 } } } impl<'a> Iterator for Data<'a> { type Item = &'a i32; fn next(&mut self) -> Option<Self::Item> { if (self.start as usize) < self.i.len(){ let r = &self.i[self.start as usize]; self.start+=1; Some(r) }else{ None } } } // 拆分两个生命周期:'a对应Data内部切片的生命周期,'b对应v的引用生命周期 struct WrapperData<'a, 'b> { v: &'b Data<'a>, } // 实现IntoIterator时,返回的Data<'a>仅关联内部切片的生命周期,与WrapperData的借用生命周期解耦 impl<'a, 'b> IntoIterator for &'b mut WrapperData<'a, 'b> { type Item = &'a i32; type IntoIter = Data<'a>; fn into_iter(self) -> Self::IntoIter { *self.v } } fn main(){ let arr = [1, 2, 3]; let d = Data{start:0,i:&arr}; let mut wr = WrapperData { v: &d }; let ddd = wr.into_iter(); // #1 let r = wr.v; // #2 正常编译通过 }
拆分生命周期后,into_iter返回的Data<'a>不再和WrapperData的可变借用绑定,方法调用结束后wr的可变借用会立即释放,后续访问wr.v就不会触发借用冲突。
内容的提问来源于stack exchange,提问作者xmh0511
相关产品推荐
相关产品推荐

