如何将闭包参数传​​递给具有生存期参数的特征方法?

时间:2018-07-06 18:10:42

标签: rust lifetime

我正在尝试使用rlua将Lua脚本添加到我的应用程序中,但是我遇到了闭包和生存期问题。

我有一个方法scope,它接受​​一个闭包作为参数。闭包采用一个参数s。我可以捕获闭包中的其他对象,甚至可以将s传递给它们的方法,前提是我已经正确设置了生存期。

我想通过使用特征INeedSForSomething来抽象这些对象,该特征提供一种接受need_s的方法s。这就是问题开始出现的地方。

scope在另一个方法do_scoped_stuff_in_a内部被调用。理想情况下,我想将Box<INeedSForSomething>传递给此方法,并以need_s作为参数调用s。问题是INeedSForSomething特性必须指定s的生存期,以便指定其与实现者内部包含的引用的另一个生存期的关系。不可能将这个生命期作为need_s方法的通用参数;如果可能的话,其余代码可以正常编译。

不幸的是,我无法弄清楚如何为INeedSForSomething提供一个与s方法之外未知的闭包参数do_scoped_stuff_in_a有关的生存期。

我想我可以通过这样写如下的生存期来确保:<'a: 'scope, 'b: 'scope, 'scope> 'scope的生存期适合闭包参数,但似乎我在这里遗漏了一些东西……

这是我写的完整代码:

struct A {
    //...
}

struct Scope {
    //...
}

impl A {
    fn scope<F>(&self, f: F)
    where
        F: FnOnce(&Scope),
    {
        let scope = Scope {};
        f(&scope);
    }
}

struct B {
    a: A,
}

impl B {
    fn do_scoped_stuff_in_a<'a: 'scope, 'b: 'scope, 'scope>(
        &'a self,
        arg: &'b Box<INeedSForSomething<'scope>>,
    ) {
        self.a.scope(|s| {
            arg.need_s(s);
        });
    }
}

trait INeedSForSomething<'scope> {
    fn need_s(&self, scope: &'scope Scope);
}

struct C<'c> {
    i_have_a_reference_with_some_lifetime: &'c String,
}

impl<'c: 'scope, 'scope> INeedSForSomething<'scope> for C<'c> {
    fn need_s(&self, s: &'scope Scope) {
        //...
    }
}
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
  --> src\main.rs:29:24
   |
29 |             arg.need_s(s);
   |                        ^
   |
note: ...the reference is valid for the lifetime 'scope as defined on the method body at 24:5...
  --> src\main.rs:24:5
   |
24 | /     fn do_scoped_stuff_in_a<'a: 'scope, 'b: 'scope, 'scope>(
25 | |         &'a self,
26 | |         arg: &'b Box<INeedSForSomething<'scope>>,
27 | |     ) {
...  |
30 | |         });
31 | |     }
   | |_____^
note: ...but the borrowed content is only valid for the anonymous lifetime #2 defined on the body at 28:22
  --> src\main.rs:28:22
   |
28 |           self.a.scope(|s| {
   |  ______________________^
29 | |             arg.need_s(s);
30 | |         });
   | |_________^

为那些熟悉rlua的人提供见识...

我正在尝试创建一个系统,在该系统中我将拥有一个特征,该特征将允许我将实现者注册为对传递给rlua::scope的闭包内实现者方法的全局回调表。我这样做不是为了使用方法创建UserData,因为我想避免说UserData必须为'static的限制。由trait声明的方法需要引用rlua::Lua对象以及传递给闭包的rlua::Scope。不幸的是,如果实现者需要另一个生存期(例如,因为它包含对其他对象的引用),则需要确保rlua::Scope的生存期在这些生存期的范围之内。这导致我不得不声明rlua::Scope的生命期作为特征声明的一部分。

也许这并不是一个好的开始,所以让我知道您是否对如何创建Lua对象有更好的想法,这将使我能够改变不是'static的Rust对象的状态。

2 个答案:

答案 0 :(得分:0)

也许您不需要一辈子;也许这段代码适合您?

impl B {
    fn do_scoped_stuff_in_a<'a: 'scope, 'b: 'scope, 'scope>(
        &'a self,
        arg: &'b Box<INeedSForSomething>,
    ) {
        self.a.scope(|s: &Scope| {
            arg.need_s(s);
        });
    }
}

trait INeedSForSomething {
    fn need_s(&self, scope: &Scope);
}

struct C<'c> {
    i_have_a_reference_with_some_lifetime: &'c String,
}

impl<'c> INeedSForSomething for C<'c> {
    fn need_s(&self, s: &Scope) {
        //...
    }
}

答案 1 :(得分:0)

我认为我试图实现的目标只是在安全的Rust中无法实现。

传递给闭包的任何参数只要有效期不长或移入其中均有效。用于传递闭包(FnOnceFnMut等)的类型签名省略了闭包引用的对象的任何通用参数(包括生存期)。鉴于这两个事实,编译器无法将传递到闭包中的参数(&Scope)的生存期与闭包引用的对象(C<'c>)的生存期参数进行比较。

这是我的意思的一个示例:让我们修改A::scope以引用一个已经创建的Scope对象,而不是在函数主体内部创建一个对象:

impl A {
    fn scope<F>(&self, s: &Scope, f: F)
    where
        F: FnOnce(&Scope),
    {
        f(s);
    }
}

我们不能保证s的生存期将被f闭包捕获的任何对象的某个生存期参数所保留,因为无法从{{1}中获取该生存期参数} FnOnce(&Scope)的签名。

我可以说在这种特殊情况下,调用闭包应该是安全的-f总是会被闭包捕获的对象的任何生存期参数所覆盖,因为它是在与闭包相同的调用中创建和销毁的被执行,使其作用域比s所引用的对象可能具有的任何生存期参数都更窄。

因此,我摆脱了所有arg的生命周期,因为它们在这种情况下是无用的,因此我尝试使用scopeunsafe转换为arg在闭包内部引用它。该解决方案对我来说效果很好。

'static

我已经在程序中实现了解决方案,以解决与rlua板条箱相关的实际问题,并且看起来也很好。