Iterators for dyn traits (lifetime problems)

Hi, I'm struggling to wrap my head around building an iterator for a dyn trait and kind of failing. The code is is supposed to iterate over two vectors and return a tuple of the element of both for each index. The Item:: is what the iterator is supposed to return, it's set by an api so I can't change it.

It looks to me like it finally does return the right type (which was what I was struggling with for a while, but now I get this

cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements

note: ...so that reference does not outlive borrowed content
note: expected std::iter::Iterator
found std::iter::Iterator

...on self.entities in this code:

let ent = self.entities.get_mut(self.count);

Any idea where I'm going wrong? Is it solvable or am I just doing this the wrong way all together? Just started coding rust yesterday after reading the docs so I'm quite new.

struct EntityIterator<'a> {
    count: usize,
    entity: &'a mut Vec<Box<dyn Entity + 'a>>,
    data: &'a Vec<Data>
}

impl<'a> EntityIterator<'a> {
    pub fn new(entities: &'a mut Vec<Box<dyn Entity + 'a>>, data: &'a Vec<Data>) -> Self {
        Self {
            count: 0,
            entities: entities,
            data: data,
        }

    }

}

impl<'a> Iterator for EntityIterator<'a> {
    type Item = (Data, &'a mut dyn Entity);

    fn next(&mut self) -> Option<Self::Item> {
        let ent = self.entities.get_mut(self.count);
        let data = self.data.get(self.count);

        match data {
            Some(x) => {
                match ent { 
                    Some(y) => 
                    {
                        let i = *x;
                        Some((i, &mut**y))
                    }
                    None => panic!("Vectors mismatch!")
                    }
            }
            None => return None 
        }
    }
}

I don't think you've copied all the code, but it looks like your trying to use EntityIterator as both a trait, and a type.

1 Like

right you are! sorry about that, made a typo when I rewrote the names to be a bit more understandable, it should be fixed now.

I’m not exactly sure what the problem is with your original code, but &’a mut Something<‘a> is generally an anti-pattern that causes the sorts of error you’re seeing. I did come up with something that compiles by using std::slice::Iter to walk over the data instead of doing it by hand (below). You should also be able to construct something very similar with a combination of Iterator::zip and other adapters without needing a custom struct.

#[derive(Clone)]
struct Data {}
trait Entity {}

use std::slice;

struct EntityIterator<'a> {
    entities: slice::IterMut<'a, Box<dyn Entity>>,
    data: slice::Iter<'a, Data>,
}

impl<'a> EntityIterator<'a> {
    pub fn new(entities: &'a mut Vec<Box<dyn Entity>>, data: &'a Vec<Data>) -> Self {
        Self {
            entities: entities.iter_mut(),
            data: data.iter(),
        }
    }
}

impl<'a> Iterator for EntityIterator<'a> {
    type Item = (Data, &'a mut dyn Entity);

    fn next(&mut self) -> Option<Self::Item> {
        use std::ops::DerefMut;
        match (self.data.next(), self.entities.next()) {
            (Some(d), Some(e)) => Some((d.clone(), e.deref_mut())),
            (None, None) => None,
            _ => panic!("Vectors mismatch!"),
        }
    }
}

(Playground)

2 Likes

Thanks! I'm not quite getting the code to compile with these changes, but it definitely gives me something to try to understand and see if that helps me get a better grasp of this. edit: ah, found the error, I had missed adding the iter-calls to the assignments in new.

Why is &'a mut Something<'a> an anti-pattern? Would really like to understand rather than just copy-pasting some code :slight_smile:

I understand the timings for the basic cases, but for cases like these my head just explodes and it feels like I start trial and erroring (or blindly following compiler recommendations) when I don't understand it, anything is likely to help :slight_smile:

I think I’ve spotted a fundamental part of the trouble. &mut references are exclusive, but your next() implementation tries to copy one: on copy stays with the iterator and the other gets returned. To write the iterator yourself, you’d need to use something like split_first_mut to split the &mut you want to return from the ones you will need to return later.


Something<‘a> effectively means that Something contains a &’a ... reference, and thus can live no longer than ’a. If it’s also borrowed for ’a, then it must live at least as long as ’a, so this construct borrows the object for its entire existence. This prevents lots of useful things being done to the borrowed object in the future, such as deconstructing it.


Edit: Just read this back, and it’s probably not very clear; hopefully someone else will come along that can explain it better :confused:

1 Like

The way I think about it is that a lifetime indicates how long a piece of memory is borrowed for. The difficulty with &’a T<‘a> is that the outer borrow must be for the exact same length of time as the inner one. Which means the borrows must start and end at the same point in time. Often its useful to have an outer lifetime that’s shorter than the inner one. This shows up in things like reborrows. At least that’s a quick reword of what @2e71828 is saying. Hopefully it’s helpful

2 Likes

Edit: Just read this back, and it’s probably not very clear; hopefully someone else will come along that can explain it better

I don't quite 100% follow but it just helped me fixing another followup error in the rest of the code so it definitely did good!

1 Like

Every little bit helps, it feels like my brain is slowly slowly beginning to assimilate this...

One final way to look at it is that by using the same lifetime you effectively have this

&’b T<‘c> where ‘b==‘c (Not real syntax)

So by specifying the lifetimes the same, you’ve effectively added an additional constraint for the compiler. And in this case, this constraint is not being done intentionally which means it’s not actually serving a purpose and ends up being counter productive as a result.

1 Like

Right, that makes total sense to me. It did however seemingly solve compile errors I didn't understand at some point (...of trial and erroring) so I kept trying it. Knowing it's a pitfall is a great help. As this is the first borrowed language I use I think my brain just needs to build some more pathways so I'm more capable of thinking in these terms. Speaking of timing... there's one thing I don't understand and only added because the compiler told me:

impl<'a> EntityIterator<'a> {

So what's the difference between the 'a in front of impl and the 'a in front of the Iterator trait here? What different lifetimes do they represent? Do I need to add it in the former to be able to use it in the latter?

Edit: Also, a big thank you for taking your time to help me out, I'm very thankful.

Angle brackets in the type system are used similarly to parenthesis in function calls; they play different roles depending on the context:

  • They can serve as an argument list, introducing the generic type names that need to be filled in: impl<‘a> .... {} and struct Foo<T> (), etc.
  • They can also be used to specify which specific types should be used for those generics in a specific situation: Vec<u32>, ...
  • They’re also used for grouping in certain situations, but those are rare.

Combining the first two, you get the relatively common construct you ended up with: impl<‘a> EntityIterator<‘a> {}. According to its definition, EntityIterator always takes a lifetime parameter. We get that lifetime by making the implementation itself generic and passing the lifetime through.

It’s perhaps easier to reason about if you think of a type that is generic over a type instead of a lfetime:

struct MyBox<Content> {
     content: Content
}

impl MyBox<String> {
    // Methods that are only valid if the box holds a String
}

impl<T> MyBox<T> {
    // Methods that are generic over the contained type T
}

// And finally, a more complex example of a generic impl
impl<T,U> AsRef<T> for MyBox<U> where U: AsRef<T> { /* ... */ }
2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.