How to spell type of closure in an implementation of a trait?

What is type of that?

core::iter::once(13_i32).map(|e| e + 10)

How to write in an implementation of a trait, taking into account all its limitation.

Full code

Of what? Of the function? Look at the docs in that case.
Otherwise, if you mean, how to write the type of a closure - you can't.

Type of returned iterator by the statement?

Is that so? If it's not possible to write down the type into a trait then maybe I can convert map-iterator further into something to make the problem solvable?

For associative types, until we get TAIT in traits or similar, you need to type erase unnameable types - coerce to a fn pointer or Box<dyn Fn> type. Or do like the standard library and write a "manual" iterator as a new struct.

For free functions, return impl Iterator<Item=..>.

3 Likes

Manual kind of adapter? Good idea, thanks, I will try that.

Examples with comments. I'm sure it's clear from the comments that I recommend the Map1b or Map3b approaches.

Another thing to keep in mind is that there are other iterator traits you might want to support/enforce, especially DoubleEndedIterator. For instance, you could change Map1b to return

-    type T1 = Box<dyn Iterator<Item=i32>>;
+    type T1 = Box<dyn DoubleEndedIterator<Item=i32>>;

But this is an API-visible change, whereas you can incrementally add such capabilities to your custom types. [1]


  1. One might say, you can more easily iterate on your struct :wink:. ↩︎

4 Likes

You can't name this type. Closures have unique non-nameable types. You can make it work if you replace closure with a regular function (closures that don't close over any variables can also be coerced to be function types.)

    fn add_10(e: i32) -> i32 { e + 10 }

    core::iter::once(13_i32).map(add_10)

then the type is Map<Once<i32>, fn(i32) -> i32>.

1 Like

Regular functions also have non-nameable types, but you've demonstrated a function pointer.

1 Like

Amazing! :smiley: Thank you.

@quinedot what about an universal solution with adapter?

trait IntoIteratorAdapter
where
    Self: Iterator + 'static,
{
    fn into_iter_adapter(self) -> IteratorAdapter<<Self as Iterator>::Item>
    where
        Self: Sized,
    {
        let iterator: Box<dyn Iterator<Item = <Self as Iterator>::Item>> = Box::new(self);
        IteratorAdapter::<<Self as Iterator>::Item> { iterator }
    }
}

Playground

And

    type T1 = IteratorAdapter<i32>;

Instead of

    // type T1 = core::iter::Map
    // <
    //   core::iter::Once< Self::Item >,
    //   &'static dyn Fn( i32 ) -> i32, /* <- what type is here? */
    // >;

Maybe it works for all cases?

Sure, should work for all 'static iterators anyway. It's a newtype around Box<dyn Iterator<Item=Item> /* + 'static */>.

If you want to name the type, this is as close as you can get, I think:

fn main() {
    let _: core::iter::Map<core::iter::Once<i32>, _> = core::iter::once(13_i32).map(|e| e + 10);
}

(Playground)

The second type parameter (_) to Map can't be named, as @kornel pointed out:

But with Map<Once<i32, _>> you can give a hint/constraint to the type inference mechanism.

Some more things you can do:

use core::iter::{Map, Once, once};

fn foo<F>(mut mapper: Map<Once<i32>, F>)
where
    F: FnMut(i32) -> i32,
{
    println!("foo got: {}", mapper.next().unwrap());
}

fn bar<M>(mut mapper: M)
where
    M: Iterator<Item = i32>,
{
    println!("bar got: {}", mapper.next().unwrap());
}

fn baz(mut mapper: Box<dyn Iterator<Item = i32>>) {
    println!("baz got: {}", mapper.next().unwrap());
}

fn main() {
    let x: Map<Once<i32>, _> = once(13_i32).map(|e| e + 10);
    foo(x);
    let x: Map<Once<i32>, _> = once(13_i32).map(|e| e + 10);
    bar(x);
    let x: Map<Once<i32>, _> = once(13_i32).map(|e| e + 10);
    baz(Box::new(x));
}

(Playground)

Output:

foo got: 23
bar got: 23
baz got: 23

1 Like

The "adapter" solution doesn't looks very good to me (reminds me of Java somehow, but I haven't been using Java for a while it feels a bit verbose). I'd go for the simpler Box<dyn Iterator<Item = i32>>, as given in my example above in function "baz". (edit: But I don't fully overlook your goals/the situation.)

Some more toying with impl, dyn, and generics:

use core::iter::{Map, Once, once};

fn more_specific_create() -> Map<Once<i32>, impl FnMut(i32) -> i32> {
    once(13_i32).map(|e| e + 10)
}

fn less_specific_create() -> impl Iterator<Item = i32> {
    once(13_i32).map(|e| e + 10)
}

/* … */

fn main() {
    foo(more_specific_create());
    bar(more_specific_create());
    baz(Box::new(more_specific_create()));
    //foo(less_specific_create()); // this won't work!
    bar(less_specific_create());
    baz(Box::new(less_specific_create()));
}

(Playground)

Output:

foo got: 23
bar got: 23
baz got: 23
bar got: 23
baz got: 23


But note that this fails:

fn main() {
    let _: core::iter::Map<core::iter::Once<i32>, impl FnMut(i32) -> i32> = core::iter::once(13_i32).map(|e| e + 10);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in variable binding
 --> src/main.rs:2:51
  |
2 |     let _: core::iter::Map<core::iter::Once<i32>, impl FnMut(i32) -> i32> = core::iter::once(13_i32).map(|e| e + 10);
  |                                                   ^^^^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0562`.
error: could not compile `playground` due to previous error

I think this is because Rust has no true support for Existential Types.

Edit: Or because closures are no true (nameable) types in Rust (I guess for performance reasons to avoid type erasure / dynamic dispatch in most cases, which, in Rust, is done explicitly with Box<dyn…> where needed).


Summarizing:

  • core::iter::once(13_i32).map(|e| e + 10) has no nameable type.
  • In some places you may write Map<Once<i32>, impl FnMut(i32) -> i32> or impl Iterator<Item = i32> as sort-of-type (but it's not allowed in all places and will require that only one such closure exists, i.e. you cannot use it to refer to different closures).
  • It's possible to use generics by using Map<Once<i32>, F> with a generic F, where F: FnMut(i32) -> i32 (function foo in my example); or by using an entirely generic M (function bar in my example), where M: Iterator<Item = i32>.
  • You can use Box::new(core::iter::once(13_i32).map(|e| e + 10)), which is a different type but can be stored as type Box<dyn Iterator<Item = i32>>.
1 Like

Turbofish only takes type names or inference holders, which impl Trait is not; also, impl Trait types aren't directly nameable yet. Neither are closure types, but you can infer it.

-    let _: Map<Once<i32>, impl FnMut(i32) -> i32> = once(13_i32).map(|e| e + 10);
+    let _: Map<Once<i32>, _> = once(13_i32).map(|e| e + 10);

Or be indirect about it.

fn foo<F: FnMut(i32) -> i32>(f: F) {
    let _: Map<Once<i32>, F> = once(13).map(f);
}

fn bar(f: impl FnMut(i32) -> i32) -> Map<Once<i32>, impl FnMut(i32) -> i32> {
    once(13).map(f)
}

Playground. Or, wait for TAIT to stabilize.

    type Closure = impl FnMut(i32) -> i32;
    let closure: Closure = |x| x;
    let _ = foo::<Closure>(closure);

Somewhat tangentially, argument-position impl Trait parameters will probably not be specifiable in turbofish, or if they someday are, it's going to be some awkward syntax. The reasons are


If you're interested in a type-theoretic take on impl Trait, see this post by Varkor about existential types in Rust.

Edit: I just gave it a skim to refresh and in the article's terms, what we have today is actually universal APIT, and the (breaking) change to late-bound would make them existential APIT. It's interesting to consider that formulation for higher-ranked types, but that would be even more off topic...

2 Likes