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

使用RAII风格守卫实现Rust词法分析器游标时遇生命周期错误

解决RAII游标守卫的Rust生命周期错误

问题背景

编写词法分析器时,希望通过RAII实现字符串游标前进/回退功能,使用CharStreamGuard作为游标索引类型:

  • 可通过new创建顶层实例
  • 可通过extend从父守卫扩展出子守卫

但编译器抛出以下生命周期错误:

error[E0597]: `csg` does not live long enough
  --> src/lib.rs:78:48
   |
76 |     let mut csg = CharStreamGuard::new(&s);
   |         ------- binding `csg` declared here
77 |     {
78 |         let mut csg2 = CharStreamGuard::extend(&mut csg);
   |                                                ^^^^^^^^ borrowed value does not live long enough
...
82 | }
   | -
   | |
   | `csg` dropped here while still borrowed
   | borrow might be used here, when `csg` is dropped and runs the `Drop` code for type `CharStreamGuard`

error[E0499]: cannot borrow `csg` as mutable more than once at a time
  --> src/lib.rs:81:5
   |
78 |         let mut csg2 = CharStreamGuard::extend(&mut csg);
   |                                                -------- first mutable borrow occurs here
...
81 |     csg.accept();
   |     ^^^
   |     |
   |     second mutable borrow occurs here
   |     first borrow later used here

原实现代码:

#[derive(PartialEq)]
enum GuardStatus {
    Unset,
    Accepted,
    Rejected,
}

pub struct CharStreamGuard<'a, 'b> {
    src: &'a String,
    parent: Option<&'b mut CharStreamGuard<'a, 'b>>,
    index: usize,
    status: GuardStatus
}

impl<'a,'b> CharStreamGuard<'a,'b> {

    fn new(src: &'a String) -> CharStreamGuard<'a,'b> {
        return CharStreamGuard {
                src: src,
                parent: None,
                index: 0,
                status: GuardStatus::Unset
        };
    }

    fn extend<'c>(parent: &'c mut CharStreamGuard<'a,'b>) -> CharStreamGuard<'a,'c>
        where 'c : 'b
    {
        let index = parent.index;
        return CharStreamGuard {
                src: parent.src,
                parent: Some(parent),
                index: index,
                status: GuardStatus::Unset
        };
    }

    fn accept(&mut self) {
        self.status = GuardStatus::Accepted;
        if let Some(parent) = self.parent.as_mut() {
            parent.index = self.index;
        }
    }

    fn reject(&mut self) {
        self.status = GuardStatus::Rejected;
    }

}

impl Drop for CharStreamGuard<'_,'_> {
    fn drop(&mut self) -> () {
        if self.status == GuardStatus::Unset {
            panic!("guard was neither accpeted nor rejected!");
        }
    }
}

fn test_it() {
    let s = String::new();
    let mut csg = CharStreamGuard::new(&s);
    {
        let mut csg2 = CharStreamGuard::extend(&mut csg);
        csg2.accept();
    }
    csg.accept();
}

错误根源

原代码的核心问题是生命周期参数设计错误:

  • 结构体CharStreamGuard<'a, 'b>中,parent字段的类型是&'b mut CharStreamGuard<'a, 'b>,形成了循环的生命周期约束
  • extend方法中'c: 'b的约束进一步混淆了编译器对借用范围的判断,导致它认为父守卫的借用会持续到自身被drop的时刻,同时无法识别子守卫作用域结束后借用已经释放

解决方案

重新设计结构体的生命周期,去掉冗余的循环约束,让子守卫的生命周期明确短于父守卫:

修改后的完整代码

#[derive(PartialEq)]
enum GuardStatus {
    Unset,
    Accepted,
    Rejected,
}

pub struct CharStreamGuard<'a> {
    src: &'a String,
    // 父守卫的引用生命周期独立于当前守卫的src生命周期
    parent: Option<&'a mut CharStreamGuard<'a>>,
    index: usize,
    status: GuardStatus,
}

impl<'a> CharStreamGuard<'a> {
    fn new(src: &'a String) -> Self {
        CharStreamGuard {
            src,
            parent: None,
            index: 0,
            status: GuardStatus::Unset,
        }
    }

    // 子守卫持有父守卫的可变引用,其生命周期与父守卫的借用周期绑定
    fn extend<'b>(parent: &'b mut CharStreamGuard<'a>) -> CharStreamGuard<'b> {
        let index = parent.index;
        CharStreamGuard {
            src: parent.src,
            parent: Some(parent),
            index,
            status: GuardStatus::Unset,
        }
    }

    fn accept(&mut self) {
        self.status = GuardStatus::Accepted;
        if let Some(parent) = self.parent.as_mut() {
            parent.index = self.index;
        }
    }

    fn reject(&mut self) {
        self.status = GuardStatus::Rejected;
    }
}

impl Drop for CharStreamGuard<'_> {
    fn drop(&mut self) {
        if self.status == GuardStatus::Unset {
            panic!("guard was neither accepted nor rejected!");
        }
    }
}

fn test_it() {
    let s = String::new();
    let mut csg = CharStreamGuard::new(&s);
    {
        let mut csg2 = CharStreamGuard::extend(&mut csg);
        csg2.accept();
    } // 子守卫作用域结束,释放对父守卫的可变借用
    csg.accept(); // 父守卫可正常被借用
}

关键修改点

  1. 简化结构体生命周期:将CharStreamGuard的生命周期参数从两个简化为一个'a,仅绑定字符串源的生命周期
  2. 明确子守卫生命周期:extend方法中,子守卫的生命周期'b与父守卫的借用周期绑定,确保子守卫的生命周期短于父守卫
  3. 修正Drop实现:去掉冗余的生命周期占位符,使用'_简化实现

这样修改后,编译器可以正确识别借用的范围,子守卫作用域结束后父守卫的借用会被释放,两个错误都会被解决。

内容的提问来源于stack exchange,提问作者user1055947

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:07:35