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


#1

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!


#2

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: http://is.gd/qVOPW5


#3

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


#4

You’re welcome :slightly_smiling:


#5

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?
http://is.gd/Cqwexb

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


#6

IterMut is a tricky case. You can for example read @Gankro’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.


#7

: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!


#8

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().)


#9

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:


#10

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()
    }
}

#11

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.


#12

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.


#13

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.