带有 Rust 的 RAII 风格防护给出无效的生命周期错误

RAII style guard with Rust gives invalid lifetime errors

提问人:user1055947 提问时间:8/13/2023 最后编辑:user1055947 更新时间:8/13/2023 访问量:45

问:

我正在编写词法分析器,并希望使用 RAII 帮助程序前进/备份游标。我有一个字符串,它由充当游标的类型索引。它是在顶层创建的,带有父保护,或者从父保护扩展而来。我相信以下代码正确地模拟了生存期:是 ' 的子生存期,但编译器仍然给出CharStreamGuardnewextend'cb

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();
}
Rust 寿命 RAII

评论

0赞 kmdreko 8/13/2023
这回答了你的问题吗?为什么这种可变借用超出了它的范围?
0赞 user1055947 8/13/2023
谢谢@kmdreko。我在那里读了你的帖子,并浏览了关于子类型和方差的 rustonomican 文章。我对它有了更好的感觉。

答:

0赞 user1055947 8/13/2023 #1

一个有效的解决方案,使用:Rc<RefCell<T>>


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

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

impl<'a> CharStreamGuard<'a> {

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

    fn extend(parent: Rc<RefCell<CharStreamGuard<'a>>>) -> CharStreamGuard<'a>
    {
        let index = parent.borrow().index;
        let src = parent.borrow().src;
        return CharStreamGuard {
                src: 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.borrow_mut().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 = Rc::new(RefCell::new(CharStreamGuard::new(&s)));
    {
        let mut csg2 = CharStreamGuard::extend(csg.clone());
        csg2.accept();
    }
    csg.borrow_mut().accept();
}