代码之家  ›  专栏  ›  技术社区  ›  oisyn

在尝试用存储闭包实现迭代器时混淆Rust生命周期问题

  •  0
  • oisyn  · 技术社区  · 9 月前

    我正在尝试编写一个简单的文本解析实用程序,用于 &str 。我想实现一个函数,该函数接受一个闭包对象和一个分隔符字符串,该字符串返回一个实现 Iterator 它不断调用闭包对象,并在每次调用时吃掉分隔符 next()

    由于我不知道迭代项是什么类型(这取决于闭包),我想对此进行概括,所以它的签名将是 FnMut(&mut Parser) -> Option<T> 。由于它可以选择从解析器存储的字符串中借用,因此我需要插入适当的生命周期。

    这是我目前所拥有的:

    ( Link to Rust Playground )

    struct Parser<'a> {
        string: &'a str,
    }
    
    impl<'a> Parser<'a> {
        // ...
        
        fn at_end(&self) -> bool {
            // returns whether we're at the end of the string
            false
        }
    
        fn expect(&mut self, _s: &str) -> &mut Self {
            // expects the next part to be `s` and advances the string
            // ...
            self
        }
    
        fn parse_iter<'b, T: 'a, F: FnMut(&'a mut Self) -> Option<T>>(
            &'a mut self,
            separator: &'b str,
            f: F,
        ) -> ParseIter<'a, 'b, T, F> {
            ParseIter {
                parser: self,
                func: f,
                sep: separator,
                skip: false,
            }
        }
    }
    
    struct ParseIter<'a, 'b, T, F>
    where
        T: 'a,
        F: FnMut(&'a mut Parser<'a>) -> Option<T>
    {
        parser: &'a mut Parser<'a>,
        func: F,
        sep: &'b str,
        skip: bool,
    }
    
    impl<'a, 'b, T, F> Iterator for ParseIter<'a, 'b, T, F>
    where
        T: 'a,
        F: FnMut(&'a mut Parser<'a>) -> Option<T>
    {
        type Item = T;
    
        fn next(&mut self) -> Option<Self::Item> {
            if self.parser.at_end() {
                return None;
            }
    
            if std::mem::replace(&mut self.skip, true) {
                self.parser.expect(self.sep);
            }
    
            (self.func)(self.parser)   // error here!
        }
    }
    

    调用闭包的最后一行代码会产生错误:

    error: lifetime may not live long enough
       --> util\src\parser.rs:156:9
        |
    140 | impl<'a, 'b, T, F> Iterator for ParseIter<'a, 'b, T, F>
        |      -- lifetime `'a` defined here
    ...
    147 |     fn next(&mut self) -> Option<Self::Item> {
        |             - let's call the lifetime of this reference `'1`
    ...
    156 |         (self.func)(self.parser)
        |         ^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a`
    

    我的意思是,我甚至不明白错误的本质。显然, &mut self 活不了多久 'a 因为它本身就是一个包含生命周期为的引用的对象 ,那么怎么能 &mut自我 可能活得更久 ?

    但是,好吧,从表面上看,我想我需要确保 &mut自我 也是 。但我不能明确地命名它,因为 迭代器 特质。

    我如何让我的代码编译?

    我想还有一个次要问题,我一直在代码中添加明确的引用来试图解决这个问题。我能逃脱的明确引用的最小数量是多少?

    1 回复  |  直到 9 月前
        1
  •  1
  •   cdhowie    9 月前

    问题源于 &'a mut Parser<'a> 这种图案 always wrong 。这可以与共享引用一起使用( &'a Parser<'a> )因为共享引用生命周期是可变的;独占引用生命周期不是。

    只需从中删除引用生存期 &mut Parser 尽可能的类型;在需要的地方引入新的寿命参数。

    struct Parser<'a> {
        string: &'a str,
    }
    
    impl<'a> Parser<'a> {
        // ...
        
        fn at_end(&self) -> bool {
            // returns whether we're at the end of the string
            false
        }
    
        fn expect(&mut self, _s: &str) -> &mut Self {
            // expects the next part to be `s` and advances the string
            // ...
            self
        }
    
        fn parse_iter<'b, 'c, T: 'a, F: FnMut(&mut Self) -> Option<T>>(
            &'c mut self,
            separator: &'b str,
            f: F,
        ) -> ParseIter<'a, 'b, 'c, T, F> {
            ParseIter {
                parser: self,
                func: f,
                sep: separator,
                skip: false,
            }
        }
    }
    
    struct ParseIter<'a, 'b, 'c, T, F>
    where
        T: 'a,
        F: FnMut(&mut Parser<'a>) -> Option<T>
    {
        parser: &'c mut Parser<'a>,
        func: F,
        sep: &'b str,
        skip: bool,
    }
    
    impl<'a, 'b, 'c, T, F> Iterator for ParseIter<'a, 'b, 'c, T, F>
    where
        T: 'a,
        F: FnMut(&mut Parser<'a>) -> Option<T>
    {
        type Item = T;
    
        fn next(&mut self) -> Option<Self::Item> {
            if self.parser.at_end() {
                return None;
            }
    
            if std::mem::replace(&mut self.skip, true) {
                self.parser.expect(self.sep);
            }
    
            (self.func)(self.parser)   // error here!
        }
    }
    

    ( Playground ))