I am trying to use rlua to add Lua scripting to my application but I've run into a problem with closures and lifetimes.
I have a method scope
that accepts a closure as its argument. The closure takes one argument s
. I can capture other objects in the closure and I can even pass s
to their methods, provided that I've set up the lifetimes properly.
I want to abstract these objects by using a trait INeedSForSomething
that provides a method need_s
that would accept s
. This is where the problems start to rise.
scope
is called inside of another method do_scoped_stuff_in_a
. Ideally, I would like to pass a Box<INeedSForSomething>
into this method and call need_s
with s
as its argument. The problem is that the INeedSForSomething
trait has to specify s
's lifetime in order to specify its relation to another lifetime of a reference contained inside the implementor. It's impossible to make this lifetime a generic parameter of need_s
method; if it was possible the rest of the code would compile fine.
Unfortunately, I can't figure out how am I supposed to provide INeedSForSomething
with a lifetime that is related to closure argument s
which is unknown outside of the do_scoped_stuff_in_a
method.
I thought I made sure that by writing lifetimes like that: <'a: 'scope, 'b: 'scope, 'scope>
the 'scope
lifetime would be suitable fo the closure argument, but it looks like I'm missing something here...
Here's the full code I wrote:
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 | | });
| |_________^
To give insight for those who are familiar with rlua...
I'm trying to create a system where I would have a trait that would allow me to register the implementor as a global table of callbacks to the methods of the implementor inside of a closure passed to rlua::scope
. I'm doing this instead of creating UserData
with methods, because I want to avoid the restriction that says UserData
must be 'static
. The method declared by the trait needs to take a reference to a rlua::Lua
object as well as rlua::Scope
passed to the closure. Unfortunately, if the implementor requires another lifetime (because it contains references to other objects for example) I need to make sure that the lifetime for rlua::Scope
is within the bounds of these lifetimes. This leads me to a situation where I have to declare rlua::Scope
's lifetime as a part of trait declaration.
Maybe this isn't a good approach to begin with, so let me know if you have any better ideas on how to create Lua objects that would allow me to mutate state of Rust's objects that are not 'static
.