如何将闭包参数传递给具有生命周期参数的特征方法?
How to pass closure argument to a method of a trait with a lifetime parameter?
我正在尝试使用 rlua 将 Lua 脚本添加到我的应用程序,但我 运行 遇到了闭包和生命周期问题。
我有一个方法 scope
接受一个闭包作为它的参数。闭包接受一个参数 s
。我可以捕获闭包中的其他对象,我什至可以将 s
传递给它们的方法,前提是我已经正确设置了生命周期。
我想通过使用特征 INeedSForSomething
来抽象这些对象,该特征 INeedSForSomething
提供了一个接受 s
的方法 need_s
。这是问题开始出现的地方。
scope
在另一个方法 do_scoped_stuff_in_a
中被调用。理想情况下,我想将 Box<INeedSForSomething>
传递给此方法,并以 s
作为参数调用 need_s
。问题是 INeedSForSomething
特性必须指定 s
的生命周期,以便指定它与包含在实现程序中的引用的另一个生命周期的关系。不可能使此生命周期成为 need_s
方法的通用参数;如果可能的话,其余的代码可以正常编译。
不幸的是,我无法弄清楚我应该如何为 INeedSForSomething
提供与闭包参数 s
相关的生命周期,而闭包参数 s
在 do_scoped_stuff_in_a
方法之外是未知的.
我想我确保通过这样写生命周期:<'a: 'scope, 'b: 'scope, 'scope>
'scope
生命周期将是 suitable 闭包参数,但看起来我遗漏了这里有东西...
这是我写的完整代码:
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 的人提供见解...
我正在尝试创建一个系统,在该系统中,我将拥有一个特征,允许我将实现者注册为全局 table 回调函数,回调函数在传递给 rlua::scope
。我这样做而不是用方法创建 UserData
,因为我想避免 UserData
必须是 'static
的限制。特征声明的方法需要引用 rlua::Lua
对象以及传递给闭包的 rlua::Scope
。不幸的是,如果实现者需要另一个生命周期(例如因为它包含对其他对象的引用),我需要确保 rlua::Scope
的生命周期在这些生命周期的范围内。这导致我不得不声明 rlua::Scope
的生命周期作为特征声明的一部分。
也许这不是一个好的开始方法,所以如果您对如何创建 Lua 对象有任何更好的想法,请告诉我,这些对象将允许我改变 Rust 对象的状态,而不是'static
.
也许你不需要一辈子;也许这段代码适合你?
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) {
//...
}
}
我认为我试图实现的目标在安全 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
闭包捕获的任何对象的某些生命周期参数都长,因为这个生命周期参数不能从 FnOnce(&Scope)
f
.
的签名
我可以说在这种特殊情况下调用闭包应该是安全的 - s
将总是比闭包捕获的对象的任何生命周期参数都长寿,因为它是在同一个调用中创建和销毁的闭包的执行赋予它比 arg
引用的对象可能具有的任何生命周期参数更窄的范围。
所以我摆脱了所有这些 scope
生命周期,因为它们在这种情况下毫无用处,我尝试使用 unsafe
将 arg
转变为 'static
就在闭包中引用它之前。该解决方案对我来说效果很好。
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>(
&self,
arg: &'a INeedSForSomething,
) {
unsafe {
let static_arg = std::mem::transmute::<&'a INeedSForSomething, &'static INeedSForSomething>(arg);
self.a.scope(|s| {
static_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) {
//...
}
}
fn main() {
let s = String::new();
let c = C {
i_have_a_reference_with_some_lifetime: &s
};
let b = B { a: A {} };
b.do_scoped_stuff_in_a(&c);
}
我已经在我的程序中实现了解决与 rlua crate 相关的实际问题的解决方案,它似乎也能正常工作。
我正在尝试使用 rlua 将 Lua 脚本添加到我的应用程序,但我 运行 遇到了闭包和生命周期问题。
我有一个方法 scope
接受一个闭包作为它的参数。闭包接受一个参数 s
。我可以捕获闭包中的其他对象,我什至可以将 s
传递给它们的方法,前提是我已经正确设置了生命周期。
我想通过使用特征 INeedSForSomething
来抽象这些对象,该特征 INeedSForSomething
提供了一个接受 s
的方法 need_s
。这是问题开始出现的地方。
scope
在另一个方法 do_scoped_stuff_in_a
中被调用。理想情况下,我想将 Box<INeedSForSomething>
传递给此方法,并以 s
作为参数调用 need_s
。问题是 INeedSForSomething
特性必须指定 s
的生命周期,以便指定它与包含在实现程序中的引用的另一个生命周期的关系。不可能使此生命周期成为 need_s
方法的通用参数;如果可能的话,其余的代码可以正常编译。
不幸的是,我无法弄清楚我应该如何为 INeedSForSomething
提供与闭包参数 s
相关的生命周期,而闭包参数 s
在 do_scoped_stuff_in_a
方法之外是未知的.
我想我确保通过这样写生命周期:<'a: 'scope, 'b: 'scope, 'scope>
'scope
生命周期将是 suitable 闭包参数,但看起来我遗漏了这里有东西...
这是我写的完整代码:
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 的人提供见解...
我正在尝试创建一个系统,在该系统中,我将拥有一个特征,允许我将实现者注册为全局 table 回调函数,回调函数在传递给 rlua::scope
。我这样做而不是用方法创建 UserData
,因为我想避免 UserData
必须是 'static
的限制。特征声明的方法需要引用 rlua::Lua
对象以及传递给闭包的 rlua::Scope
。不幸的是,如果实现者需要另一个生命周期(例如因为它包含对其他对象的引用),我需要确保 rlua::Scope
的生命周期在这些生命周期的范围内。这导致我不得不声明 rlua::Scope
的生命周期作为特征声明的一部分。
也许这不是一个好的开始方法,所以如果您对如何创建 Lua 对象有任何更好的想法,请告诉我,这些对象将允许我改变 Rust 对象的状态,而不是'static
.
也许你不需要一辈子;也许这段代码适合你?
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) {
//...
}
}
我认为我试图实现的目标在安全 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
闭包捕获的任何对象的某些生命周期参数都长,因为这个生命周期参数不能从 FnOnce(&Scope)
f
.
我可以说在这种特殊情况下调用闭包应该是安全的 - s
将总是比闭包捕获的对象的任何生命周期参数都长寿,因为它是在同一个调用中创建和销毁的闭包的执行赋予它比 arg
引用的对象可能具有的任何生命周期参数更窄的范围。
所以我摆脱了所有这些 scope
生命周期,因为它们在这种情况下毫无用处,我尝试使用 unsafe
将 arg
转变为 'static
就在闭包中引用它之前。该解决方案对我来说效果很好。
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>(
&self,
arg: &'a INeedSForSomething,
) {
unsafe {
let static_arg = std::mem::transmute::<&'a INeedSForSomething, &'static INeedSForSomething>(arg);
self.a.scope(|s| {
static_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) {
//...
}
}
fn main() {
let s = String::new();
let c = C {
i_have_a_reference_with_some_lifetime: &s
};
let b = B { a: A {} };
b.do_scoped_stuff_in_a(&c);
}
我已经在我的程序中实现了解决与 rlua crate 相关的实际问题的解决方案,它似乎也能正常工作。