[Solved] Can't impl IntoIterator trait. Lifetime param + associated type issues

Ok, I'm pretty new to Rust, so this might be a pretty stupid issue, but I'm banging my head against it and for the life of me I can't figure out how to make this work.

Long story short, I want to implement the IntoIterator trait for an Iterator I have, so that I can use the "for in" loop syntax with it. But no matter what I do, I can't make all of these things work out at the same time:

  • I have to define the IntoIter associated type for IntoIterator
  • The iterator type has a lifetime parameter because it has a reference to the container
  • I can't define a lifetime parameter in an associated type without the compiler saying that "the lifetime parameter 'a is not constrained by the impl trait, self type, or predicates"

I have uploaded a gist here with a sample showing the issue: https://gist.github.com/jonvaldes/e4abcbb250bf14a3041f

I'd be super grateful if anyone could help me figure this out!

Thanks in advance!

That's because into_iter takes self by value, but you are implementing IntoIterator as if it took self by reference. A correct implementation is for &'a V3: Rust Playground

1 Like

Oh, I didn't see that one coming!
Thank you sooo much!

You're welcome :slightly_smiling:

I'm risking you getting bored with me here, but I'm once again fighting the type checker and some help would be much appreciated.

I'm now trying to add a mutable iterator, and the compiler gets terribly confused by that, saying it "cannot infer an appropriate lifetime for autoref due to conflicting requirements". And then it suggests a solution which doesn't actually compile because it doesn't meet the Iterator trait's requirements.

Any idea what I could do with this?

P.S: People keep telling me they ended up getting used to the type checker, but man this is obtuse!

IterMut is a tricky case. You can for example read @Gankra's thoughts about it.

In this case, I think you can't safely write the mutable iterator like this, since the lifetime of the reference it hands out is not tied to the iterator: Rust cannot guarantee that you're doing the right thing with self.i - you might return a reference to the same index twice, which would create an aliased mutable reference.

:weary: Sigh...

That'll teach me to not try to do fancy stuff, and just stick to coding in C-style in whatever language I'm using that day...

Thanks for the help!

I'm not saying it can't be done - I'm far from an expert in lifetimes. But that's what I pieced together.

(In this case, you might want to change the internal representation of V3 to [f32; 3] which should give you the slice iterator impls for free. The downside would be not being able to access .x etc, only as .x().)

Yeah, or just use repr(C,packed) and go berserk in unsafe mode creating a slice from the memory location of 'x', and then getting an iterator from that :joy:

Well, it does actually work. Who needs thinking when you have unsafe!

#[derive(Copy, Clone, Default)]
#[repr(C,packed)]
pub struct V3 {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

impl V3 {
    pub fn as_slice<'a>(&'a self) -> &'a [f32] {
        unsafe {
            let data_ptr: *const f32 = mem::transmute(&self.x);
            slice::from_raw_parts(data_ptr, 3)
        }
    }

    pub fn as_slice_mut<'a>(&'a mut self) -> &'a mut [f32] {
        unsafe {
            let data_ptr: *mut f32 = mem::transmute(&self.x);
            slice::from_raw_parts_mut(data_ptr, 3)
        }
    }

    pub fn iter<'a>(&'a self) -> slice::Iter<'a, f32> {
        self.as_slice().iter()
    }

    pub fn iter_mut<'a>(&'a mut self) -> slice::IterMut<'a, f32> {
        self.as_slice_mut().iter_mut()
    }
}

Although this is spectacurlarly unsafe :wink: once you allow unsafe, you can just transmute away the lifetime in your original attempt.

        let result = if self.i < 3 {
            Some(unsafe { ::std::mem::transmute(self.v.index_mut(self.i)) })
        } else {
            None
        };

etc.

1 Like

It seems like a compiler bug that Rust complained about the lifetime error, and never about the signature of the impl's into_iter not matching the trait's definition. That message would have led straight to the problem.

It would error on both, but the checks seem to run in passes and the trait implementation correctness test is probably running after the lifetime use correctness test. That being said, I agree that it would be more helpful if it was the other way around.