Non-dyn iterable?

I am trying to avoid lifetimes because I still don't have a good understanding of the concept. I am reading this wonderful article and it clarifies many misunderstandings. Although I am not sure I can solve the problem.

I know how to implement iterable collection on dyn.

Playground

My attempt to implement the same code without dyn.

Playground

I was thinking about using impls but there is a restriction on using it in a trait.
What is wrong with the code?
What other useful articles can you recommend?

Since the returned iterator type cannot be explicitly named (due to the closure type in Map), avoiding the dyn in this kind of code is not really possible without something like the unstable type_alias_impl_trait feature.

I'm on mobile at the moment, so I can't explain how that would work in detail.

1 Like

I'm trying it here and it's neat.

And I tried GAT here and it's probably not what OP want.

1 Like

Note that if you write the first example without any lifetime hints, the error includes:

help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound
   |
32 |     fn elements(&self) -> Box<dyn Iterator<Item = (i32, &String)> + '_> {
   |                                                                   

which does indeed work.

You can absolutely tie yourself in knots -avoiding- using lifetimes, but mostly that's restricted to structs with lifetime parameters. In my experience methods are generally pretty easy, and often you can just not declare anything and the error will tell you what to do.

1 Like

Well, GAT might be wanted if the trait is defined elsewhere. So subtle.

1 Like

@vague your last iteration on the solution gives almost perfect solution. And it works if trait is defined externally. One minor thing is ability to use interface of the trait Enumerable by the trait itself.

fn sum(&self) -> i32 {
    self.elements().fold(0, |acc, x| acc + x.0)
}

This does not work with such generic definition:

type It<'it>
where
    Self: 'it;

Instead, I modified it and defined GAT like that:

type It<'it>: Iterator<Item = (i32, &'it String)>
where
    Self: 'it;

Now that works! Thank you!

@vague @steffahn @simonbuchan can we conclude, there is no solution in Stable Rust for today?

Final solution
Related discussion on StackOverflow.

@simonbuchan thanks for your hint!

Yeah, these features, in particular type_alias_impl_trait, are in popular demand for a reason. While working around the lack of GATs is possible for types generic over lifetimes alone (which is the case here), type_alias_impl_trait really does bring new capabilites onto the table in this case, similar to how impl Trait in return types enabled more efficient (i.e. trait-object-less) implementations of (non-trait-)functions/methods returning iterators, closures, or similar types, when they were introduced (see e.g. RFCs 1522 and 1951).

2 Likes

Aha! So workaround exists. I am wondering what it is? I am trying to understand your answer to similar question.


By the way I had been struggling with an events handling mechanism similar one you mentioned without success. I knew this things are related. I am happy I can get back to the problem after improving my understanding of all that.

In case that wasn't clear, my answer was addressing the fact that your last posted solution uses

#![feature(generic_associated_types)]
#![feature(type_alias_impl_trait)]

and I'm saying that there exist solutions/workarounds to avoid the need for generic_associated_types, but (as far as I'm aware) there are no solutions/workarounds (other than using dyn trait objects) to avoid the need for type_alias_impl_trait.

Aha.

@steffahn @vague Is that possible to implement IntoIterrator trait iterating references on entries for that either with nightly or without? My attempt was not successful.

Playgorund

impl<'it> IntoIterator for &'it Container {
    type Item = (&'it i32, &'it String);
    type IntoIter = std::collections::hash_map::Iter<'it, i32, String>;

    fn into_iter(self) -> Self::IntoIter {
        self.map.iter()
    }
}
1 Like

I mean... to qualify this, it certainly depends on what your goal is. You can simply avoid using the ordinary Iterator::map method so you don't get "unnamable" types. Or use function pointers or Box<dyn FnMut> trait objects (well that adds trait objects again, so maybe that doesn't count) in the Iterator::map call. Or use your own custom map-like method with a custom FnMut-like trait that can be implemented by hand; then hand-implement it instead of using a closure. In the end of a day it's a question of what level of convenience is or isn't required, but closures aren't all that magical under the hood.

The comparable story of Futures instead of closures is different in this regard. That's e.g. why async methods in traits are really essentially impossible without using Box<dyn Future...>. And you cannot really (realistically) hand-implement all the kinds of futures that async fn or async {} blocks give you, because the self-referential nature of those types gets really tricky and unsafe really quick (and hand-writing the state machines is a hassle, too).

1 Like

I see. Thank you :slightly_smiling_face:

Also, I should note, that if possible to avoid using lifetimes you can implement IntoIterator for your &Container:

impl< 'it > IntoIterator for &'it Container
{
  type Item = ( &'it i32, &'it String );
  type IntoIter = std::collections::hash_map::Iter< 'it, i32, String >;
  fn into_iter( self ) -> Self::IntoIter
  {
    self.map.iter()
  }
}

Full solution of a tweaked problem

Because the lifetime is dropped that works even on stable Rust.
Most probably you want to have your own InotIterator-like trait, especially if there is more than a single way how can you iterate your container, but if not you can simply implement standard IntoIterator for reference.