Impossible to pass diverging closures as an argument
fn banana<F: FnOnce()>(f: F) -> ! { f(); loop {} }
fn main() {
banana(move || -> ! { loop {} })
}
result
error[E0271]: type mismatch resolving `<[closure@<anon>:6:12: 6:36] as std::ops::FnOnce<()>>::Output == ()`
--> <anon>:6:5
|
6 | banana(move || -> ! { loop {} })
| ^^^^^^ expected !, found ()
|
= note: expected type `!`
= note: found type `()`
= note: required by `banana`
Used to work in 1.8.0, so is a regression from stable to stable.
@eddyb says its a bugfix; worked around with
fn banana<R, F: FnOnce() -> R>(f: F) -> R { f() }Simonas Kazlauskas at 2017-01-13 13:32:08
I am not 100% sure though. cc @canndrew @nikomatsakis
Eduard-Mihai Burtescu at 2017-01-13 14:20:19
Reopening for discussion.
Simonas Kazlauskas at 2017-01-13 16:01:12
Well, I suppose that would not be expected to work, if the true return type of the closure here is
!(as opposed to saying that the return type of the closure is a free variable that can be any type, including!). The latter might make more sense though?Niko Matsakis at 2017-01-13 19:19:56
But, you declared the closure type
Fto return(), did you not? This code typechecks:#![feature(never_type)] fn banana<F: FnOnce() -> !>(f: F) -> ! { f(); loop {} } fn main() { banana(move || -> ! { loop {} }) }Alex Burka at 2017-01-13 20:18:58
@durka the original code used to work on stable rustc. Presence of a
featurein your “typechecks” example prevents that. I already pointed out what was a sufficient workaround for me in the 2nd comment.
Something like
fn banana(f: fn()) -> ! { f(); loop {} } fn diverging() -> ! { loop {} } fn main() { banana(diverging) }does not work either, which is consistent with the “bug fix” thinking, but it would be nice if there was a way to somehow encode code like in @durka’s comment above (i.e.
bananais explicitly diverging function, whatFis bounded on does not matter) in stable rust (e.g. by stabilising!?).That being said, I’m somewhat surprised by this behaviour. I would have expected that
-> !would allow passing a diverging function to any argument of typefn(...) -> *, as!is supposed to coerce to anything, function will never return etc.Simonas Kazlauskas at 2017-01-14 05:44:31
I would have expected that
-> !would allow passing a diverging function to any argument of typefn(...) -> *, as!is supposed to coerce to anything, function will never return etc.In order to support that sort of behavior we'd need to make
!a subtype of everything. An expression of type!can coerce to an expression of typeTbut that doesn't mean you can use a type which implementsFn() -> !in place of a type which implementsFn() -> ().I don't know why that code ever worked on stable rust but, from memory, diverging closures were never well-behaved until the
feature(never_type)changes.Andrew Cann at 2017-01-14 06:59:41
Today:
error[E0271]: type mismatch resolving `<[closure@src\main.rs:4:12: 4:36] as std::ops::FnOnce<()>>::Output == ()` --> src\main.rs:4:5 | 1 | fn banana<F: FnOnce()>(f: F) -> ! { f(); loop {} } | -------- required by this bound in `banana` ... 4 | banana(move || -> ! { loop {} }) | ^^^^^^ expected `()`, found `!` | = note: expected unit type `()` found type `!`Donough Liu at 2020-05-04 00:17:28