为什么移动闭包不会捕获具有通用类型的值?
Why will a move closure not capture a value with a generic type?
我正在尝试创建一个 ScopeRunner 类型,它可以存储对实现范围特征的类型方法的方法调用,如下所示:
trait Scope {
fn run(&self) -> String;
}
struct ScopeImpl;
impl Scope for ScopeImpl {
fn run(&self) -> String {
"Some string".to_string()
}
}
struct ScopeRunner {
runner: Box<dyn Fn() -> String>,
}
impl ScopeRunner {
fn new<S: Scope>(scope: S) -> Self {
ScopeRunner {
runner: Box::new(move || scope.run())
}
}
pub fn run(self) -> String {
(self.runner)()
}
}
fn main() {
let scope = ScopeImpl {};
let scope_runner = ScopeRunner::new(scope);
dbg!(scope_runner.run());
}
我预计,由于 ScopeRunner::new
创建了一个移动闭包,这将导致作用域被移动到闭包中。但是借用检查器却给了我这个错误:
error[E0310]: the parameter type `S` may not live long enough
--> src/main.rs:21:30
|
20 | fn new<S: Scope>(scope: S) -> Self {
| -- help: consider adding an explicit lifetime bound `S: 'static`...
21 | ScopeRunner {runner: Box::new(move || scope.run())}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...so that the type `[closure@src/main.rs:21:39: 21:58 scope:S]` will meet its required lifetime bounds
--> src/main.rs:21:30
|
21 | ScopeRunner {runner: Box::new(move || scope.run())}
|
当我用只需要 ScopeImpl
的非通用版本替换 ScopeRunner::new
时,此代码确实有效。
fn new(scope: ScopeImpl) -> Self {
ScopeRunner {
runner: Box::new(move || scope.run())
}
}
我不明白为什么会这样。对我来说,通用 Scope
的生命周期似乎与具体版本相同。
问题是 S
可以是具有 Scope
impl 的任何类型,其中包括携带对其他类型的引用的所有类型 not-yet-existing 类型。例如你可以有这样的实现:
struct AnotherScope<'a> {
reference: &'str,
}
impl Scope for ScopeImpl {
fn run(&self) -> String {
self.reference.to_string()
}
}
Rust 很谨慎,希望确保这适用于 任何 符合条件的 S
,包括它是否包含引用。
最简单的解决方法是按照错误说明的建议进行操作,只是禁止 S
有任何 non-static 引用:
fn new<S: Scope + 'static>(scope: S) -> Self {
ScopeRunner {
runner: Box::new(move || scope.run())
}
}
边界 S
与 'static
实际上意味着 S
可以包含对具有 'static
生命周期的值的引用或根本不包含任何引用。
如果您想更灵活一点,可以将其扩展到比 ScopeRunner
本身更长的引用:
struct ScopeRunner<'s> {
runner: Box<dyn Fn() -> String + 's>,
}
impl<'s> ScopeRunner<'s> {
fn new<S: Scope + 's>(scope: S) -> Self {
ScopeRunner {
runner: Box::new(move || scope.run())
}
}
pub fn run(self) -> String {
(self.runner)()
}
}
我正在尝试创建一个 ScopeRunner 类型,它可以存储对实现范围特征的类型方法的方法调用,如下所示:
trait Scope {
fn run(&self) -> String;
}
struct ScopeImpl;
impl Scope for ScopeImpl {
fn run(&self) -> String {
"Some string".to_string()
}
}
struct ScopeRunner {
runner: Box<dyn Fn() -> String>,
}
impl ScopeRunner {
fn new<S: Scope>(scope: S) -> Self {
ScopeRunner {
runner: Box::new(move || scope.run())
}
}
pub fn run(self) -> String {
(self.runner)()
}
}
fn main() {
let scope = ScopeImpl {};
let scope_runner = ScopeRunner::new(scope);
dbg!(scope_runner.run());
}
我预计,由于 ScopeRunner::new
创建了一个移动闭包,这将导致作用域被移动到闭包中。但是借用检查器却给了我这个错误:
error[E0310]: the parameter type `S` may not live long enough
--> src/main.rs:21:30
|
20 | fn new<S: Scope>(scope: S) -> Self {
| -- help: consider adding an explicit lifetime bound `S: 'static`...
21 | ScopeRunner {runner: Box::new(move || scope.run())}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...so that the type `[closure@src/main.rs:21:39: 21:58 scope:S]` will meet its required lifetime bounds
--> src/main.rs:21:30
|
21 | ScopeRunner {runner: Box::new(move || scope.run())}
|
当我用只需要 ScopeImpl
的非通用版本替换 ScopeRunner::new
时,此代码确实有效。
fn new(scope: ScopeImpl) -> Self {
ScopeRunner {
runner: Box::new(move || scope.run())
}
}
我不明白为什么会这样。对我来说,通用 Scope
的生命周期似乎与具体版本相同。
问题是 S
可以是具有 Scope
impl 的任何类型,其中包括携带对其他类型的引用的所有类型 not-yet-existing 类型。例如你可以有这样的实现:
struct AnotherScope<'a> {
reference: &'str,
}
impl Scope for ScopeImpl {
fn run(&self) -> String {
self.reference.to_string()
}
}
Rust 很谨慎,希望确保这适用于 任何 符合条件的 S
,包括它是否包含引用。
最简单的解决方法是按照错误说明的建议进行操作,只是禁止 S
有任何 non-static 引用:
fn new<S: Scope + 'static>(scope: S) -> Self {
ScopeRunner {
runner: Box::new(move || scope.run())
}
}
边界 S
与 'static
实际上意味着 S
可以包含对具有 'static
生命周期的值的引用或根本不包含任何引用。
如果您想更灵活一点,可以将其扩展到比 ScopeRunner
本身更长的引用:
struct ScopeRunner<'s> {
runner: Box<dyn Fn() -> String + 's>,
}
impl<'s> ScopeRunner<'s> {
fn new<S: Scope + 's>(scope: S) -> Self {
ScopeRunner {
runner: Box::new(move || scope.run())
}
}
pub fn run(self) -> String {
(self.runner)()
}
}