Rust中调用结构体不可变方法后如何修改结构体?
问题:Rust Cursor的take方法编译报错处理
我尝试编译以下Rust代码:
pub enum Lexem<'a> { Identifier(&'a str), } pub struct Cursor<'a> { lexems: Vec<&'a str>, offset: usize } impl<'a> Cursor<'a> { pub fn new(lexems: Vec<&str>) -> Cursor { Cursor { lexems, offset: 0 } } pub fn peek(&self) -> Option<&str> { if self.offset < self.lexems.len() { Some(&self.lexems[self.offset]) } else { None } } pub fn take(&mut self) -> Option<&str> { if let Some(lexem) = self.peek() { self.offset += 1; Some(lexem) } else { None } } }
编译器报错如下:
error[E0506]: cannot assign to `self.offset` because it is borrowed --> <source>:28:13 | 26 | pub fn take(&mut self) -> Option<&str> { | - let's call the lifetime of this reference `'1` 27 | if let Some(lexem) = self.peek() { | ---- `self.offset` is borrowed here 28 | self.offset += 1; | ^^^^^^^^^^^^^^^^ `self.offset` is assigned to here but it was already borrowed 29 | Some(lexem) | ----------- returning this value requires that `*self` is borrowed for `'1`
我清楚错误原因:编译器认为修改offset会导致peek返回的引用失效,但实际不会。请问如何让这段代码通过编译?
解决方案
方法1:直接在take中实现peek逻辑
避免调用peek带来的借用冲突,把判断逻辑直接写在take里:
pub fn take(&mut self) -> Option<&str> { if self.offset < self.lexems.len() { let lexem = &self.lexems[self.offset]; self.offset += 1; Some(lexem) } else { None } }
这种方式简单直接,适合逻辑不复杂的场景,唯一缺点是存在少量代码重复。
方法2:拆分引用,明确互不干扰
先单独获取lexems的不可变引用,再处理offset,让编译器识别到两个操作不会互相影响:
pub fn take(&mut self) -> Option<&str> { let lexems = &self.lexems; if self.offset < lexems.len() { let lexem = &lexems[self.offset]; self.offset += 1; Some(lexem) } else { None } }
这里lexems的借用和offset的可变借用被拆分,编译器能正确判断安全性。
方法3:显式指定peek返回的生命周期
将peek返回的引用生命周期绑定到'a(即lexems中字符串的生命周期),而非self的借用周期:
pub fn peek(&self) -> Option<&'a str> { if self.offset < self.lexems.len() { Some(self.lexems[self.offset]) } else { None } } pub fn take(&mut self) -> Option<&'a str> { if let Some(lexem) = self.peek() { self.offset += 1; Some(lexem) } else { None } }
这样编译器会明白返回的引用来自lexems中的字符串,修改offset不会让这个引用失效。
方法4:使用unsafe代码(不推荐)
若你能确保逻辑绝对安全,可以用unsafe绕过借用检查,但这会失去Rust的内存安全保障,仅在必要时使用:
pub fn take(&mut self) -> Option<&str> { if let Some(lexem) = self.peek() { unsafe { let ptr = &mut self.offset as *mut usize; *ptr += 1; } Some(lexem) } else { None } }
内容的提问来源于stack exchange,提问作者Poseydon
相关产品推荐
相关产品推荐

