我正在尝试使用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对象的状态。
答案 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中无法实现。
传递给闭包的任何参数只要有效期不长或移入其中均有效。用于传递闭包(FnOnce
,FnMut
等)的类型签名省略了闭包引用的对象的任何通用参数(包括生存期)。鉴于这两个事实,编译器无法将传递到闭包中的参数(&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
的生命周期,因为它们在这种情况下是无用的,因此我尝试使用scope
将unsafe
转换为arg
在闭包内部引用它。该解决方案对我来说效果很好。
'static
我已经在程序中实现了解决方案,以解决与rlua板条箱相关的实际问题,并且看起来也很好。