rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
95.19k stars 12.28k forks source link

Nested impl Trait for higher-order functions #76410

Open nwtgck opened 3 years ago

nwtgck commented 3 years ago

Hi!

It will be nice to use impl Trait with higher-order functions like the following. But it occurs a compile error at impl Iterator<Item=u32>.

fn f1<F: FnMut(u32) -> u32>(a: u32) -> impl FnOnce(F) -> impl Iterator<Item=u32> /* <- compile error! */ {
    move |f: F| {
        (0..a).map(f)
    }
}

fn main() {
    let iter = f1(10)(|b| b * 2);
    for x in iter {
        println!("{:}", x);
    }
}

Nested impl Trait: https://doc.rust-lang.org/error-index.html#E0666

expected behavior/work around

Here is an expected behavior and a work around. We need to write the type explicitly like std::iter::Map<std::ops::Range<u32>, F> instead of impl Iterator<Item=u32>.

fn f1_with_explicit_type<F: FnMut(u32) -> u32>(a: u32) -> impl FnOnce(F) -> std::iter::Map<std::ops::Range<u32>, F> {
    move |f: F| {
        (0..a).map(f)
    }
}

fn main() {
    let iter = f1_with_explicit_type(10)(|b| b * 2);
    for x in iter {
        println!("{:}", x);
    }
}

[Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&code=fn%20f1_with_explicit_type%3CF%3A%20FnMut(u32)%20-%3E%20u32%3E(a%3A%20u32)%20-%3E%20impl%20FnOnce(F)%20-%3E%20std%3A%3Aiter%3A%3AMap%3Cstd%3A%3Aops%3A%3ARange%3Cu32%3E%2C%20F%3E%20%7B%0A%20%20%20%20move%20%7Cf%3A%20F%7C%20%7B%0A%20%20%20%20%20%20%20%20(0..a).map(f)%0A%20%20%20%20%7D%0A%7D%0A%0Afn%20main()%20%7B%0A%20%20%20%20let%20iter%20%3D%20f1_with_explicit_type(10)(%7Cb%7C%20b%20*%202)%3B%0A%20%20%20%20for%20x%20in%20iter%20%7B%0A%20%20%20%20%20%20%20%20println!(%22%7B%3A%7D%22%2C%20x)%3B%0A%20%20%20%20%7D%0A%7D%0A)

The output should be the following.

0
2
4
6
8
10
12
14
16
18

It will be harder to write and read the return type when we append other methods after the .map(f).

toothbrush7777777 commented 3 years ago

If you read the page you linked (https://doc.rust-lang.org/error-index.html#E0666), you need to define the type as a named generic parameter:

fn f1<F: FnMut(u32) -> u32, T: impl Iterator<Item=u32>>(a: u32) -> impl FnOnce(F) -> T {
    move |f: F| {
        (0..a).map(f)
    }
}

fn main() {
    let iter = f1(10)(|b| b * 2);
    for x in iter {
        println!("{:}", x);
    }
}
nwtgck commented 3 years ago

@toothbrush7777777 Actually, I did. I think you mean the following, not T: impl Iterator...

fn f1_with_named_generic<F: FnMut(u32) -> u32, T: Iterator<Item=u32>>(a: u32) -> impl FnOnce(F) -> T {
    move |f: F| {
        (0..a).map(f)
    }
}

Then, we will have a compile error as follows. So, I don't think a named generic parameter works...

error[E0308]: mismatched types
  --> src/main.rs:31:9
   |
29 | fn f1_with_named_generic<F: FnMut(u32) -> u32, T: Iterator<Item=u32>>(a: u32) -> impl FnOnce(F) -> T {
   |                                                - this type parameter
30 |     move |f: F| {
31 |         (0..a).map(f)
   |         ^^^^^^^^^^^^^ expected type parameter `T`, found struct `std::iter::Map`
   |
   = note: expected type parameter `T`
                      found struct `std::iter::Map<std::ops::Range<u32>, F>`
nwtgck commented 3 years ago

Another work around

Here is another work around with type_alias_impl_trait in Nightly Rust.

#![feature(type_alias_impl_trait)]

type IterU32<T> = impl Iterator<Item=u32>;

fn f1_with_impl_trait_alias<F: FnMut(u32) -> u32>(a: u32) -> impl FnOnce(F) -> IterU32<F> {
    move |f: F| {
        (0..a).map(f)
    }
}

fn main() {
    let iter = f1_with_impl_trait_alias(10)(|b| b * 2);
    for x in iter {
        println!("{:}", x);
    }
}

[Rust Playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&code=%23!%5Bfeature(type_alias_impl_trait)%5D%0A%0Atype%20IterU32%3CT%3E%20%3D%20impl%20Iterator%3CItem%3Du32%3E%3B%0A%0Afn%20f1_with_impl_trait_alias%3CF%3A%20FnMut(u32)%20-%3E%20u32%3E(a%3A%20u32)%20-%3E%20impl%20FnOnce(F)%20-%3E%20IterU32%3CF%3E%20%7B%0A%20%20%20%20move%20%7Cf%3A%20F%7C%20%7B%0A%20%20%20%20%20%20%20%20(0..a).map(f)%0A%20%20%20%20%7D%0A%7D%0A%0Afn%20main()%20%7B%0A%20%20%20%20let%20iter%20%3D%20f1_with_impl_trait_alias(10)(%7Cb%7C%20b%20*%202)%3B%0A%20%20%20%20for%20x%20in%20iter%20%7B%0A%20%20%20%20%20%20%20%20println!(%22%7B%3A%7D%22%2C%20x)%3B%0A%20%20%20%20%7D%0A%7D%0A)

ref: https://stackoverflow.com/a/39490692/2885946