Skip to content

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

Closed
@arduano

Description

@arduano

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

Activity

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[/+] on Sep 18, 2022
zachs18

zachs18 commented on Sep 18, 2022

@zachs18
Contributor

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

jpalaciosdev commented on Sep 20, 2022

@jpalaciosdev

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

compiler-errors commented on Sep 20, 2022

@compiler-errors
Member

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

Seems to be a problem with RPIT inference in borrowck

moved this to Can do after stabilization in type alias impl trait stabilizationon Sep 21, 2022
self-assigned this
on Sep 21, 2022
WaffleLapkin

WaffleLapkin commented on Oct 31, 2022

@WaffleLapkin
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) {}
Repository owner moved this from Can do after stabilization to Done in type alias impl trait stabilizationon Oct 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

C-bugCategory: This is a bug.

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @oli-obk@compiler-errors@zachs18@jpalaciosdev@arduano

      Issue actions

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