Rust Borrow Checker:如何从可变结构体的不可变成员进行不可变借用?
解决Rust中同时借用不可变数据与修改可变数据的问题
你的核心问题在于:当前Parser结构体的生命周期绑定导致,当你持有结构体的可变引用时,整个结构体都会被独占,无法同时借用其中的不可变tokens。下面提供三种可行的解决思路:
方法1:拆分可变与不可变数据(推荐)
直接把可变的计数器从结构体中分离出来,让tokens和counter可以被分别独立借用,完全符合Rust的所有权规则,且无额外开销。
pub struct Parser<'a> { pub tokens: &'a Vec<&'a str>, } pub fn advance(tokens: &Vec<&str>, counter: &mut usize) -> &str { *counter += 1; tokens[*counter - 1] } pub fn build_expression(tokens: &Vec<&str>, counter: &mut usize) -> Vec<&str> { let there = advance(tokens, counter); let once = advance(tokens, counter); let was = advance(tokens, counter); vec![there, once, was] } fn main() { let sentence = "there once was a lady who swallowed a fly".to_string(); let words = vec![ &sentence[0..5], &sentence[6..10], &sentence[11..14], &sentence[15..16], &sentence[17..21], &sentence[22..25], &sentence[26..35], &sentence[36..37], &sentence[38..41], ]; println!("{:?}", words); let mut counter = 0; let expr = build_expression(&words, &mut counter); println!("{:?}", expr); }
方法2:使用内部可变性(适合需要封装结构体的场景)
用RefCell包裹计数器,将可变借用的检查从编译期推迟到运行期,这样可以在持有结构体不可变引用的同时修改内部的计数器。注意仅适用于单线程场景。
use std::cell::RefCell; pub struct Parser<'a> { pub tokens: &'a Vec<&'a str>, pub counter: RefCell<usize>, } pub fn advance<'a>(p: &'a Parser) -> &'a str { let mut counter = p.counter.borrow_mut(); *counter += 1; p.tokens[*counter - 1] } pub fn build_expression<'a>(p: &'a Parser) -> Vec<&'a str> { let there = advance(p); let once = advance(p); let was = advance(p); vec![there, once, was] } fn main() { let sentence = "there once was a lady who swallowed a fly".to_string(); let words = vec![ &sentence[0..5], &sentence[6..10], &sentence[11..14], &sentence[15..16], &sentence[17..21], &sentence[22..25], &sentence[26..35], &sentence[36..37], &sentence[38..41], ]; println!("{:?}", words); let p = Parser { tokens: &words, counter: RefCell::new(0) }; let expr = build_expression(&p); println!("{:?}", expr); }
方法3:调整生命周期绑定
修改函数的生命周期标注,让返回的引用直接绑定到tokens的生命周期,而非整个Parser的可变引用,这样借用检查器会识别出:修改计数器不会影响tokens的借用。
pub struct Parser<'a> { pub tokens: &'a Vec<&'a str>, pub counter: usize, } // 返回值生命周期绑定到tokens的'a,而非Parser的可变引用 pub fn advance<'a>(p: &mut Parser<'a>) -> &'a str { p.counter += 1; p.tokens[p.counter - 1] } pub fn build_expression<'a>(p: &mut Parser<'a>) -> Vec<&'a str> { let there = advance(p); let once = advance(p); let was = advance(p); vec![there, once, was] } fn main() { let sentence = "there once was a lady who swallowed a fly".to_string(); let words = vec![ &sentence[0..5], &sentence[6..10], &sentence[11..14], &sentence[15..16], &sentence[17..21], &sentence[22..25], &sentence[26..35], &sentence[36..37], &sentence[38..41], ]; println!("{:?}", words); let mut p = Parser { tokens: &words, counter: 0 }; let expr = build_expression(&mut p); println!("{:?}", expr); // 现在可以同时使用expr(引用自tokens)和修改counter p.counter = 0; let another_expr = build_expression(&mut p); println!("{:?}", another_expr); }
内容的提问来源于stack exchange,提问作者iwans
相关产品推荐
相关产品推荐

