如何指定依赖于单独类型的闭包的借用绑定的生命周期?

Kit*_*tes 6 lifetime rust

我有两种类型:LexerSFunction.

SFunction 代表有状态函数,并且定义如下:

struct SFunction {
    f: Option<Box<FnMut() -> SFunction>>, 
}
Run Code Online (Sandbox Code Playgroud)

重要的是,任何SFunction引用一个返回一个的闭包SFunction.

现在我希望每个影响相同的函数都具有这些函数Lexer.这意味着每个SFunctions都必须具有取决于具体的寿命Lexer.

如果你想更多地了解我正在做的事情,这里还有一些代码:

impl Lexer {
    fn lex(&mut self) {
        self.sfunction(Lexer::lexNormal).call()
    }

    fn sfunction(&mut self, f: fn(&mut Lexer) -> SFunction) -> SFunction {

        SFunction::new(Box::new(|| f(self)))
        // SFunction { f: Some(Box::new(move ||f(self))) }
    }

    fn lexNormal(&mut self) -> SFunction {
        return SFunction::empty()
    }
}
Run Code Online (Sandbox Code Playgroud)

(这是Rust操场中代码的完整版本.)

如何在代码中指定此生命周期要求?

我得到的编译器错误"无法推断self由于需求冲突而导致闭包捕获的适当生命周期".我很确定这里的"冲突要求"是一种Box类型假定生命周期'static.我可以做这样的事情Box<FnMut() -> SFunction + 'a>哪里'a是它取决于词法定义了一辈子,但我不知道如何定义这样的'a.

谢谢你的帮助!

She*_*ter 4

问题出在这一行:

SFunction::new(Box::new(|| f(self)))
Run Code Online (Sandbox Code Playgroud)

这里,self是对 a 的引用Lexer,但不能保证词法分析器的寿命足够长。事实上,它需要活一辈子'static!如果没有指定生命周期,装箱特征对象将使用该'static生命周期。用代码来说,这两个声明是等价的:

<Box<FnMut() -> SFunction>>
<Box<FnMut() -> SFunction> + 'static>
Run Code Online (Sandbox Code Playgroud)

您可以通过限制代码只接受终生有效的引用来编译代码(以一种不令人满意的方式)'static

fn lex(&'static mut self) {
    self.sfunction(Lexer::lex_normal).call()
}

fn sfunction(&'static mut self, f: fn(&mut Lexer) -> SFunction) -> SFunction {
    SFunction::new(Box::new(move || f(self)))
}
Run Code Online (Sandbox Code Playgroud)

当然,您是否拥有Lexer静态生命周期是非常值得怀疑的,因为这意味着它正在对静态数据进行词法分析,这并不是很有用。这意味着我们需要在您的特征对象中包含生命周期......正如您所建议的那样。

最终有助于解决问题的是稍微重组你的闭包:

fn sfunction(&mut self, f: fn(&mut Lexer) -> SFunction) -> SFunction {
    SFunction::new(Box::new(move || {
        // f(self)
        let s2 = self;
        let f2 = f;
        f2(s2)
    }))
}
Run Code Online (Sandbox Code Playgroud)

编译此代码会产生一个错误,该错误指出了真正问题:

<anon>:31:22: 31:26 error: cannot move out of captured outer variable in an `FnMut` closure [E0507]
<anon>:31             let s2 = self;
                               ^~~~
<anon>:31:17: 31:19 note: attempting to move value to here
<anon>:31             let s2 = self;
                          ^~
<anon>:31:17: 31:19 help: to prevent the move, use `ref s2` or `ref mut s2` to capture value by reference
Run Code Online (Sandbox Code Playgroud)

我相信这是因为FnMut闭包可能会被多次调用,这意味着需要复制闭包中包含的引用,这将是一个坏消息,因为&mut引用应该是唯一的。

总而言之,这段代码可以工作:

struct SFunction<'a> {
    f: Option<Box<FnOnce() -> SFunction<'a> + 'a>>, 
}

impl<'a> SFunction<'a> {
    fn new(f: Box<FnOnce() -> SFunction<'a> + 'a>) -> SFunction<'a> {
        SFunction {
            f: Some(f),
        }
    }

    fn empty() -> SFunction<'a> {
        SFunction {
            f: None,
        }
    }

    fn call(self) { }
}

struct Lexer;

impl Lexer {
    fn lex(&mut self) {
        self.sfunction(Lexer::lex_normal).call()
    }

    fn sfunction(&mut self, f: fn(&mut Lexer) -> SFunction) -> SFunction {
        SFunction::new(Box::new(move || f(self)))
    }

    fn lex_normal<'z>(&'z mut self) -> SFunction<'z> {
        SFunction::empty()
    }
}

fn main() {
    let mut l = Lexer;
    l.lex()
}
Run Code Online (Sandbox Code Playgroud)

我希望我的解释是正确的,并且更改后的代码仍然适合您的用例!