Trait for iterator that was implemented for a reference

Hi folks,

Could you please tell me if the following is possible?

(the commented code doesn't work)

Thank you :wink:

#[derive(Debug, PartialEq)]
pub struct Content {
    number: i32,
}
#[derive(Debug, PartialEq)]
pub struct Thing {
    content: Content,
}
#[derive(Debug, PartialEq)]
pub struct ManyThings {
    things: Vec<Thing>,
}

pub struct ManyThingsIterator {
    things_reversed: Vec<Thing>,
}
pub struct ManyThingsIteratorSlice<'a> {
    things: &'a [Thing],
}
impl Iterator for ManyThingsIterator {
    type Item = Thing;

    fn next(&mut self) -> Option<Self::Item> {
        self.things_reversed.pop()
    }
}
impl<'a> Iterator for ManyThingsIteratorSlice<'a> {
    type Item = &'a Thing;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some((out, rest)) = self.things.split_first() {
            self.things = rest;
            Some(out)
        } else {
            None
        }
    }
}

impl IntoIterator for ManyThings {
    type Item = Thing;
    type IntoIter = ManyThingsIterator;

    fn into_iter(self) -> Self::IntoIter {
        let mut things_reversed = self.things;
        things_reversed.reverse();
        ManyThingsIterator { things_reversed }
    }
}
impl<'a> IntoIterator for &'a ManyThings {
    type Item = &'a Thing;
    type IntoIter = ManyThingsIteratorSlice<'a>;

    fn into_iter(self) -> Self::IntoIter {
        ManyThingsIteratorSlice {
            things: self.things.as_slice(),
        }
    }
}

pub trait GetContent {
    fn move_it_out(self) -> Content;
    fn get_it(&self) -> &Content;
}
pub trait GetContentMultiple {
    fn move_it_out_multiple<T>(self) -> Vec<Content>
    where
        Self: Sized + IntoIterator<Item = T>,
        T: GetContent,
    {
        self.into_iter().map(|inp| inp.move_it_out()).collect()
    }
    fn get_it_multiple<'a, T>(self) -> Vec<&'a Content>
    where
        Self: Sized + IntoIterator<Item = &'a T>,
        T: GetContent + 'a,
    {
        self.into_iter().map(|inp| inp.get_it()).collect()
    }
}

impl GetContent for Thing {
    fn move_it_out(self) -> Content {
        self.content
    }
    fn get_it(&self) -> &Content {
        &self.content
    }
}
impl GetContentMultiple for ManyThings {}

#[test]
fn test_move_it_out_multiple() {
    let a = ManyThings {
        things: vec![
            Thing {
                content: Content { number: 0 },
            },
            Thing {
                content: Content { number: 1 },
            },
            Thing {
                content: Content { number: 2 },
            },
        ],
    };
    assert_eq!(
        a.move_it_out_multiple(),
        vec![Content { number: 0 }, Content { number: 1 }, Content { number: 2 }]
    )
}
#[test]
fn test_get_it_multiple() {
    let a = ManyThings {
        things: vec![
            Thing {
                content: Content { number: 0 },
            },
            Thing {
                content: Content { number: 1 },
            },
            Thing {
                content: Content { number: 2 },
            },
        ],
    };
    let b = &a;
    // assert_eq!(
    //     b.get_it_multiple(),
    //     vec![&Content { number: 0 }, &Content { number: 1 }, &Content { number: 2 }]
    // )
}

Add this and it works:

impl GetContentMultiple for &ManyThings {}
1 Like

I didn't realize I can do

impl GetContentMultiple for ManyThings {}
impl GetContentMultiple for &ManyThings {}

That's great! Thank you!

Your OP is answered, however, you might want to end up reworking your approach anyway. Due to how method resolution works, if you try to call get_it_multiple on a ManyThings value,[1] it's going to try to select the ManyThings implementation and not the &ManyThings implementation. Then the call will (1) try to take ownership, which you probably don't want, but also (2) not compile anyway, because the bounds on the method aren't met.

Example.

Alternatives include splitting up the trait and mirroring the IntoIterator patterns for Self and &Self more directly by taking &self for the borrowing method (adjusting the bounds to match, and no longer needing to implement the trait for &ManyThings).


Unrelated optimization.

 pub struct ManyThingsIterator {
-    things_reversed: Vec<Thing>,
+    things: std::vec::IntoIter<Thing>,
 }

 impl Iterator for ManyThingsIterator {
     type Item = Thing;

     fn next(&mut self) -> Option<Self::Item> {
-        self.things_reversed.pop()
+        self.things.next()
     }
 }

 impl IntoIterator for ManyThings {
     type Item = Thing;
     type IntoIter = ManyThingsIterator;

     fn into_iter(self) -> Self::IntoIter {
-        let mut things_reversed = self.things;
-        things_reversed.reverse();
-        ManyThingsIterator { things_reversed }
+        let things = self.things.into_iter();
+        ManyThingsIterator { things }
    }
}

  1. i.e. not a &ManyThings value â†Šī¸Ž

3 Likes

Thank you very much @quinedot for the unrelated optimization and the alternative solutions:

This is the best:

    fn get_it_multiple<'a, T>(&'a self) -> Vec<&'a Content>
    where
        &'a Self: Sized + IntoIterator<Item = &'a T>,
        T: GetContent + 'a,
    {
        self.into_iter().map(|inp| inp.get_it()).collect()
    }

Thank you.