Rust:为什么将闭包绑定到变量会改变类型?

jm4*_*ier 9 closures rust

我有这个(最简化的)代码:

fn returns_closure() -> Box<dyn Fn(&u64)> {
    let closure = |_| ();
    Box::new(closure)
}
Run Code Online (Sandbox Code Playgroud)

这不会编译并带有相当无用的错误消息:

error[E0308]: mismatched types
 --> src/main.rs:3:5
  |
3 |     Box::new(closure)
  |     ^^^^^^^^^^^^^^^^^ one type is more general than the other
  |
  = note: expected type `FnOnce<(&u64,)>`
             found type `FnOnce<(&u64,)>`
Run Code Online (Sandbox Code Playgroud)

但是,当我不先将闭包绑定到变量,而是直接在 Box 的构造函数中创建它时,它确实会编译:

error[E0308]: mismatched types
 --> src/main.rs:3:5
  |
3 |     Box::new(closure)
  |     ^^^^^^^^^^^^^^^^^ one type is more general than the other
  |
  = note: expected type `FnOnce<(&u64,)>`
             found type `FnOnce<(&u64,)>`
Run Code Online (Sandbox Code Playgroud)

为什么第一个编译失败,两者有什么区别?


编辑:埃蒙斯的回答似乎是正确的。我用 nightly 工具链 (1.52.0) 编译了完全相同的代码,得到了一个更好的错误:

error: implementation of `FnOnce` is not general enough
 --> src/main.rs:3:5
  |
3 |     Box::new(closure)
  |     ^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
  |
  = note: closure with signature `fn(&'2 u64)` must implement `FnOnce<(&'1 u64,)>`, for any lifetime `'1`...
  = note: ...but it actually implements `FnOnce<(&'2 u64,)>`, for some specific lifetime `'2`


Run Code Online (Sandbox Code Playgroud)

Ema*_*oun 5

正如@frankenapps 指出的,如果您使用let closure = |_: &u64|();,则不会收到此错误。那么发生了什么?

为了尝试解决这个问题,我进行了以下使用显式生命周期的编辑(而不是让编译器为我们删除它们),以获得相同的错误消息:

fn returns_closure<'a>() -> Box<dyn Fn(&'_ u64)> {
    let closure = |_: &'a u64| ();
    Box::new(closure)
}
Run Code Online (Sandbox Code Playgroud)

运行此代码会给出以下错误消息:

rror[E0308]: mismatched types
 --> src/lib.rs:4:5
  |
4 |     Box::new(closure)
  |     ^^^^^^^^^^^^^^^^^ one type is more general than the other
  |
  = note: expected type `for<'r> Fn<(&'r u64,)>`
             found type `Fn<(&'a u64,)>`
note: this closure does not fulfill the lifetime requirements
 --> src/lib.rs:3:19
  |
3 |     let closure = |_: &'a u64| ();
  |                   ^^^^^^^^^^^^^^^

error[E0308]: mismatched types
 --> src/lib.rs:4:5
  |
4 |     Box::new(closure)
  |     ^^^^^^^^^^^^^^^^^ one type is more general than the other
  |
  = note: expected type `FnOnce<(&u64,)>`
             found type `FnOnce<(&'a u64,)>`
note: this closure does not fulfill the lifetime requirements
 --> src/lib.rs:3:19
  |
3 |     let closure = |_: &'a u64| ();
  |                   ^^^^^^^^^^^^^^^

Run Code Online (Sandbox Code Playgroud)

错误消息并不完全相同,因为我们使用的是显式生命周期,但错误类型仍然是one type is more general than the other.

所以我们在这里看到的可能是生命周期省略中的错误。您可以看到,我为闭包赋予了与函数 ( 'a) 相同的生命周期,但函数返回dyn Fn(&'_ u64)。这里请注意,它'_是任何生命周期的替代品,而不仅仅是'a。因此,返回类型&'_ u64比闭包 ( ) 更通用(接受更多生命周期)&'a u64,并且我们得到了错误。

我尝试了其他生命周期组合,但这是唯一出现此类错误的组合。但我不能说这是否真的是正在发生的事情。