#Returning a (wrapped) reference from an iterator

23 messages ยท Page 1 of 1 (latest)

sinful cobalt
#

A thousand times asked for sure but I wasn't able to figure this out even with an example...

I have this:

pub struct Frames<'a> {
    gfx: &'a mut Gfx,
}

impl<'a> Iterator for Frames<'a> {
    type Item = Frame<'a>;

    fn next(&'a mut self) -> Option<Self::Item> {
        Some(Frame{ gfx: self.gfx })
    }
}

pub struct Frame<'a> {
    pub gfx: &'a mut Gfx
}

And the error is that the lifetime 'a defined on the fn next(&'a mut self) line doesn't necessarily outlive the lifetime 'a defined at impl<'a> Iterator for Frames<'a>. I have a feeling any attempts at specifying the lifetime for self is going to be doomed because of the trait definition but I'm not entirely sure, and if I make it just fn next(&mut self) it says I'm returning a reference with a lifetime of '1 when 'a is expected, so whole another lifetime in that case ๐Ÿ˜•

I'm not sure I actually understand why with &'a mut self the lifetime is required to outlive the 'a of impl<'a>. I'm probably having two lifetimes with identical names here?

#

Here are the errors as spoken by the compiler, too (first one with &'a mut self, second with &mut self):

error[E0308]: method not compatible with trait
  --> src\gfx.rs:73:5
   |
73 |     fn next(&'a mut self) -> Option<Self::Item> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected signature `fn(&mut Frames<'a>) -> Option<_>`
              found signature `fn(&'a mut Frames<'a>) -> Option<_>`
note: the anonymous lifetime as defined here...
  --> src\gfx.rs:73:5
   |
73 |     fn next(&'a mut self) -> Option<Self::Item> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime `'a` as defined here
  --> src\gfx.rs:70:6
   |
70 | impl<'a> Iterator for Frames<'a> {
   |      ^^
error: lifetime may not live long enough
  --> src\gfx.rs:74:9
   |
70 |   impl<'a> Iterator for Frames<'a> {
   |        -- lifetime `'a` defined here
...
73 |       fn next(&mut self) -> Option<Self::Item> {
   |               - let's call the lifetime of this reference `'1`
74 | /         Some(Frame{
75 | |             gfx: self.gfx
76 | |         })
   | |__________^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
spare veldt
#

It has to tie the lifetime of the returned object to the lifetime of the borrow to self so it knows not to take another mutable borrow to self while it has that element (so it doesn't end up giving out multiple mutable borrows of the frame - which would happen if .next were called two times in a row)

But then by adding the lifetime annotations it doesn't match the Iterator trait anymore

So you'd actually need to be able to change the Iterator trait to look more like

trait Iterator {
        type Item<'a>;
        fn next(&mut self) -> Option<Self::Item<'_>>;
}

But you can't change it because you don't own the source code

There is a crate for lending iterators which reimplements a lot of the Iterator methods but allows for this

You might be able to do it with unsafe code, if you track the lended status yourself and return None if it's not dropped yet or there might be another way I'm not aware of in safe code

sinful cobalt
#

Ah :( That is most unfortunate
Really unfortunate we're stuck with pre-GAT iterator traits

#

Thanks

meager lynx
sinful cobalt
#

That is true
I would be more than fine with restricting this to the for frame in frames { ... } syntax but maybe this is just not possible

meager lynx
#

so it can fundamentally never work out

#

you're better off creating some kind of traversal api

sinful cobalt
#

I'm just going to replace this with a frames.for_each(|frame| { ... }) I suppose

meager lynx
meager lynx
#

(or with the ControlFlow example)

sinful cobalt
#

Yeah ๐Ÿค”

#

The ControlFlow example doesn't feel exactly elegant either but eeeehh

meager lynx
#

the answer is pub fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> { ferrisOwO

sinful cobalt
#

Or what winit does and basically frames.for_each(|frame, should_stop: &mut bool| { ... })
so you don't have to return a value unless you specifically want to stop

#

it really shouldn't bug me this much that I can't have the for loop syntax ๐Ÿ˜‚ thanks for your input

meager lynx
#

i think potentially in the future we could have a lending iterator do something like that

#

now that GATs are a thing

sinful cobalt
#

Yeah

#

I checked the lending iterator crate too and it mentioned the overlapping mut refs problem

#

I'd say I wonder how they'd solve that but I don't have the energy to think about that anymore