Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fn trait doesn't allow impl returns (impl Fn() -> impl Trait), which is inconsistent with all other traits #101968

Closed
arduano opened this issue Sep 18, 2022 · 4 comments
Assignees
Labels
C-bug Category: This is a bug.

Comments

@arduano
Copy link

arduano commented Sep 18, 2022

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

@arduano arduano added the C-bug Category: This is a bug. label Sep 18, 2022
@arduano arduano changed the title Fn trait doesn't allow impl returns, which is inconsistent with all other traits Fn trait doesn't allow impl returns (impl Fn() -> impl Trait), which is inconsistent with all other traits Sep 18, 2022
@zachs18
Copy link
Contributor

zachs18 commented Sep 18, 2022

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)

@jpalaciosdev
Copy link

jpalaciosdev commented Sep 20, 2022

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`.

Link to playground.

@compiler-errors
Copy link
Member

@jpalaciosdev: That regressed in #96727 cc @oli-obk

Seems to be a problem with RPIT inference in borrowck

@WaffleLapkin
Copy link
Member

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) {}

@oli-obk oli-obk closed this as completed Oct 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug.
Development

No branches or pull requests

6 participants