Reputation: 1778
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:
use std::collections::HashMap;
pub trait Enumerable {
fn elements<'a, 'b>(&'a self) -> Box<dyn Iterator<Item = (i32, &String)> + 'b>
where
'a: 'b;
}
#[derive(Debug)]
struct Container {
pub map: HashMap<i32, String>,
}
impl Enumerable for Container {
fn elements<'a, 'b>(&'a self) -> Box<dyn Iterator<Item = (i32, &String)> + 'b>
where
'a: 'b,
{
Box::new(self.map.iter().map(|el| (*el.0, el.1)))
}
}
My attempt to implement the same code without dyn
. Playground:
use std::collections::HashMap;
pub trait Enumerable<'it, 'it2, It>
where
It: Iterator<Item = &'it2 (i32, &'it String)>,
'it: 'it2,
{
fn elements<'a, 'b>(&'a self) -> It
where
'a: 'b;
}
#[derive(Debug)]
struct Container {
pub map: HashMap<i32, String>,
}
impl<'it, 'it2> Enumerable<'it, 'it2, core::slice::Iter<'it, (i32, &String)>> for Container {
fn elements<'a, 'b>(&'a self) -> core::slice::Iter<'a, (i32, &'b String)>
where
'a: 'b,
{
self.map.iter().map(|el| (*el.0, el.1))
}
}
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?
Upvotes: 0
Views: 113
Reputation: 1778
There is a solution to the original problem with GAT and TAIT which are not part of stable channgel for today.
Solution is
mod mod1 {
pub trait Enumerable {
type It<'it>: Iterator<Item = (i32, &'it String)>
where
Self: 'it;
fn elements(&self) -> Self::It<'_>;
}
}
//
impl mod1::Enumerable for Container {
type It<'it> = impl Iterator<Item = (i32, &'it String)>;
fn elements(&self) -> Self::It<'_> {
self.map.iter().map(|el| (*el.0, el.1))
}
}
There are alternative solutions, but this one works even if the trait is not part of your crate.
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.
Upvotes: 2
Reputation: 22738
Apart of the fact that I don't think you need two separate lifetimes 'a
and 'b
, your first code example already looks quite promising.
Then, once you have only one lifetime, Rust can figure out lifetimes without any annotations:
use std::collections::HashMap;
pub trait Enumerable {
fn elements(&self) -> Box<dyn Iterator<Item = (i32, &String)> + '_>;
}
#[derive(Debug)]
struct Container {
pub map: HashMap<i32, String>,
}
impl Enumerable for Container {
fn elements(&self) -> Box<dyn Iterator<Item = (i32, &String)> + '_> {
Box::new(self.map.iter().map(|el| (*el.0, el.1)))
}
}
I know this is an XY-problem answer, but maybe it helps anyway. Your main reasoning behind not using dyn
was to not deal with lifetimes, so I thought this might be relevant.
Upvotes: 3