You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 14:35:26