-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
Consider the following function which returns a closure impl
fn returns_closure(hmm: bool) -> impl Fn(i32) -> i32 {
if hmm {
|x| x + 1
} else {
|x| x * 2
}
}
fn main() {
println!("{}", returns_closure(true)(10));
println!("{}", returns_closure(false)(10));
}
This builds and runs providing the following output:
11
20
This seems reasonable at first glance, however consider a changed version:
fn returns_closure(hmm: bool, y: u32) -> impl Fn(i32) -> i32 {
if hmm {
|x| x + y
} else {
|x| x * y
}
}
fn main() {
println!("{}", returns_closure(true, 3)(10));
println!("{}", returns_closure(false, 4)(10));
}
This fails to build with:
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:5:9
|
2 | / if hmm {
3 | | |x| x + y
| | --------- expected because of this
4 | | } else {
5 | | |x| x * y
| | ^^^^^^^^^ expected closure, found a different closure
6 | | }
| |_____- `if` and `else` have incompatible types
|
= note: expected type `[closure@src/main.rs:3:9: 3:18]`
found closure `[closure@src/main.rs:5:9: 5:18]`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
For more information about this error, try `rustc --explain E0308`.
The first example uses two closures and compiles and runs fine, demonstrating we do have two closures of the same type which the output from the second example states cannot happen.
I suspect what's happening is the first closures don't capture from the environment so are seen as function pointers both with type fn(i32) -> i32
. So this is just an issue of clarifying the second error message e.g. 'no two closure which capture the environment, even if identical, have the same type'.
Changing the first example as follows seems to confirm this:
fn returns_closure(hmm: bool) -> fn(i32) -> i32 {
if hmm {
|x| x + 1
} else {
|x| x * 2
}
}
fn main() {
println!("{}", returns_closure(true)(10));
println!("{}", returns_closure(false)(10));
}
(Builds and runs with identical results to the first example)
From reading the rust reference you could say 'closure' inherently means environment capture (i.e. |x| x + 1
is not a closure), but that conflicts with language used in the rust book so could lead to confusion.
If it's intended that closures from the first example should have different types there's something more serious going on with the type inference/checking.
rustc --version --verbose
:
rustc 1.56.0-nightly (ccffcafd5 2021-08-11)
binary: rustc
commit-hash: ccffcafd55e58f769d4b0efc0064bf65e76998e4
commit-date: 2021-08-11
host: x86_64-unknown-linux-gnu
release: 1.56.0-nightly
LLVM version: 12.0.1