rust
rust copied to clipboard
Fn trait doesn't allow impl returns (impl Fn() -> impl Trait), which is inconsistent with all other traits
I've been working on a functional interface for a library which relies on code generation, so I have functions returning functions, however I've hit a roadblock with functions returning closures which return traits (without resorting to dynamic dispatch).
The compiler doesn't allow impl Fn() -> impl Trait
while allowing impl Trait<T = impl Trait>
which seems inconsistent.
Here is my minimal reproduction code:
// This works of course
fn closure() -> impl Fn() -> bool {
let f = || true;
f
}
// Error
// `impl Trait` only allowed in function and inherent method return types, not in `Fn` trait return
fn future_closure() -> impl Fn() -> impl Future<Output = bool> {
let f = || async { true };
f
}
// Same error
fn future_closure_arg(arg: impl Fn() -> impl Future<Output = bool>) {}
// This works though
fn iter_impl(arg: impl Iterator<Item = impl Future<Output = bool>>) {}
However, seems like wrapping the Fn in another trait fixes the error, so this doesn't appear to be a functionality limitation but more of an oversight:
// This compiles fine
trait Func {
type T;
fn call(&self) -> Self::T;
}
impl<F: Fn() -> R, R> Func for F {
type T = R;
fn call(&self) -> Self::T {
self()
}
}
fn future_trait_closure() -> impl Func<T = impl Future<Output = bool>> {
let f = || async { true };
f
}
Meta
rustc --version --verbose
:
rustc 1.63.0 (4b91a6ea7 2022-08-08)
binary: rustc
commit-hash: 4b91a6ea7258a947e59c6522cd5898e7c0a6a88f
commit-date: 2022-08-08
host: x86_64-unknown-linux-gnu
release: 1.63.0
LLVM version: 14.0.5
Note that writing impl Fn<(), Output = impl Future<Output = bool>>
with #![feature(unboxed_closures)]
does not give the error, so perhaps its something to do with how Fn() -> Ret
syntax is parsed or converted to a bound?
(link to playground)
Note that writing
impl Fn<(), Output = impl Future<Output = bool>>
with#![feature(unboxed_closures)]
does not give the error...
Just a quick note: compilation still fails for some code using the latest nightly builds (though it used to compile fine with older ones, such as nightly-2022-06-29). For example:
#![feature(unboxed_closures)]
use std::future::Future;
fn less_than<'a>(number: &'a i32) -> impl FnOnce<(i32,), Output = impl Future<Output = bool> + 'a> {
move |n: i32| async move { n < *number }
}
fn main() {
let zero = 0;
let less_than_zero = less_than(&zero);
}
Compilation output:
error: concrete type differs from previous defining opaque type use
--> src/lib.rs:6:19
|
6 | move |n: i32| async move { n < *number }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `impl Future<Output = bool>`, got `impl Future<Output = bool>`
|
note: previous use here
--> src/lib.rs:6:5
|
6 | move |n: i32| async move { n < *number }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0720]: cannot resolve opaque type
--> src/lib.rs:5:67
|
5 | fn less_than<'a>(number: &'a i32) -> impl FnOnce<(i32,), Output = impl Future<Output = bool> + 'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot resolve opaque type
For more information about this error, try `rustc --explain E0720`.
@jpalaciosdev: That regressed in #96727 cc @oli-obk
Seems to be a problem with RPIT inference in borrowck
A bit of an update: future_closure
from the original issue now works with #![feature(impl_trait_in_fn_trait_return)]
(tracking issue):
#![feature(impl_trait_in_fn_trait_return)]
use core::future::Future;
fn future_closure() -> impl Fn() -> impl Future<Output = bool> {
let f = || async { true };
f
}
Can this be closed then?
As a side note in argument position it still doesn't work (because we may want to special case fn trait syntax to work better with lifetimes...), however it's easy to transform APIT into a generic:
fn future_closure_arg(arg: impl Fn() -> impl Future<Output = bool>) {}
// =>
fn future_closure_arg<Fut: Future<Output = bool>>(arg: impl Fn() -> Fut) {}