Home > Software design >  How do I specify the type for `map` in `IntoIterator` impl?
How do I specify the type for `map` in `IntoIterator` impl?

Time:07-10

I have the following code:

struct Container<T>(T);

struct MyStruct<T> {
    entries: Vec<T>,
}

impl<'a, T> IntoIterator for &'a MyStruct<Container<T>> {
    type Item = &'a Container<T>;
    type IntoIter = std::slice::Iter<'a, Container<T>>;

    fn into_iter(self) -> Self::IntoIter {
        let entries = &self.entries;
        entries.into_iter()
    }
}

fn main() {
    let my_struct: MyStruct<Container<usize>> = MyStruct {
        entries: vec![
            Container(0),
            Container(1),
            Container(2),
            Container(3),
            Container(4),
        ],
    };
    
    for c in my_struct.into_iter() {
        println!("{}", c.0);
        // I want c to be usize not Container<usize>.
        println!("{}", c); 
    }
}

I would like the iterator obtained from into_iter() to produce the inner value of Container.

I tried like this:

impl<'a, T> IntoIterator for &'a MyStruct<Container<T>> {
    type Item = &'a T;
    type IntoIter = std::slice::Iter<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        let entries = &self.entries;
        
        // Map to the inner value.
        entries.into_iter().map(|c| c.0)
    }
}

But it doesn't compile with the error below:

error[E0308]: mismatched types
  --> src/main.rs:25:9
   |
21 |     fn into_iter(self) -> Self::IntoIter {
   |                           -------------- expected `std::slice::Iter<'_, T>` because of return type
...
25 |         entries.into_iter().map(|c| c.0)
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::slice::Iter`, found struct `Map`
   |
   = note: expected struct `std::slice::Iter<'_, T>`
              found struct `Map<std::slice::Iter<'_, Container<T>>, [closure@src/main.rs:25:33: 25:40]>`

If I follow the suggestions I get more and more complicated errors.

Rust playground code

CodePudding user response:

This works:

struct Container<T>(T);

struct MyStruct<T> {
    entries: Vec<T>,
}

impl<T> IntoIterator for MyStruct<Container<T>> {
    type Item = T;
    type IntoIter = std::iter::Map<std::vec::IntoIter<Container<T>>, fn(Container<T>) -> T>;

    fn into_iter(self) -> Self::IntoIter {
        self.entries.into_iter().map(|c| c.0)
    }
}

fn main() {
    let my_struct: MyStruct<Container<usize>> = MyStruct {
        entries: vec![Container(0), Container(1), Container(2), Container(3)],
    };

    for c in my_struct.into_iter() {
        println!("{}", c);
    }
}

CodePudding user response:

You could implement Deref instead, and then you would only need to add some stars (*) to deref enough:

use std::ops::Deref;

struct Container<T>(T);

impl<T> Deref for Container<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

struct MyStruct<T> {
    entries: Vec<T>,
}

impl<T> Deref for MyStruct<T> {
    type Target = Vec<T>;
    fn deref(&self) -> &Self::Target {
        &self.entries
    }
}

fn main() {
    let my_struct: MyStruct<Container<usize>> = MyStruct {
        entries: vec![Container(0), Container(1), Container(2), Container(3)],
    };

    for c in my_struct.iter() {
        println!("{}", c.0);
        println!("{}", **c);
    }
}
  • Related