Common type of iterators

Hi, I try to iterate over a single item or an given iterator, depending on input data, but the compiler tells me both iterators have different types. Why isn't std::slice::Iter of type impl Iterator? Is there any trick I can apply to make this code compile?

struct BusinessLogic {
    data: Vec<u32>,
}

impl BusinessLogic {
    fn get_ids(&self) -> impl Iterator<Item = u32> + '_ {
        self.data.iter().map(|x| x + 1)
    }
}

fn main() {
    let bl = BusinessLogic {
        data: vec![1, 2, 3],
    };
    let iterable = if true {
        bl.get_ids()
    } else {
        [1].iter()
    };
    
    for element in iterable {
        println!("{}", element);
    }
}

Playground link: Rust Playground

1 Like

Unifying bl.get_ids() and [1].iter() as the same type would require looking at what type is behind the impl Iterator returned by get_ids(). This is exactly what opaque types disallow. When you write impl Iterator all consumers will know that some type implementing Iterator is returned, but by design they won't know which type.

Another thing is that bl.get_ids() and [1].iter() return truly different types even if impl Iterator were to be transparent. bl.get_ids() returns iter::Map<vec::Iter<u32>, {closure}> while [1].iter() returns slice::Iter<u32>. These are different types with potentially (in this case definitively) different sizes, so you can't assign both to the same variable.

2 Likes

You can combine/unify different iterator types using enums or trait objects. An enum that can help is Either from the either crate.

fn main() {
    let bl = BusinessLogic {
        data: vec![1, 2, 3],
    };
    let iterable = if true {
        Left(bl.get_ids())
    } else {
        Right(iter::once(1))
    };
    
    for element in iterable {
        println!("{}", element);
    }
}

(playground)
The item type must still match, so I’ve replaced [1].iter() with iter::once(1) that has u32 items, not &u32. You could also use <_>::into_iter([1]) or [1].iter().copied().

Trait objects, using the type dyn Iterator<Item = u32>, need some form of indirection: Either by boxing the iterator

fn main() {
    let bl = BusinessLogic {
        data: vec![1, 2, 3],
    };
    let iterable: Box<dyn Iterator<Item = u32>> = if true {
        Box::new(bl.get_ids())
    } else {
        Box::new(iter::once(1))
    };
    
    for element in iterable {
        println!("{}", element);
    }
}

(playground)

or by placing in some dedicated local variables and creating a mutable reference

fn main() {
    let bl = BusinessLogic {
        data: vec![1, 2, 3],
    };
    let (mut iter1, mut iter2);
    let iterable: &mut dyn Iterator<Item = u32> = if true {
        iter1 = bl.get_ids();
        &mut iter1
    } else {
        iter2 = iter::once(1);
        &mut iter2
    };
    
    for element in iterable {
        println!("{}", element);
    }
}

(playground)

2 Likes

Because impl Iterator in return position means that the function has a single, defined, return type that you don't want or can't name, but it lets the caller know that that type implements the Iterator. That anonymous type however is not std::slice::Iter, and even if it was, the caller is not allowed to know that.

1 Like

To put it in another perspective:

Because impl Trait is not a type. It is a marker that stands in for a type, which is known to the compiler but unknown to the programmer.

(I'm primarily contrasting it with dyn Trait, which is a proper, first-class type in its own right.)

3 Likes

Thank you all very much, it seems obvious now and in theory I should have been able to understand that on my own, but sometimes one needs to be pushed a little bit in the right direction. I don't know yet which solution I'll go with, but at least I understood the problem. Thanks again!

Edit: It is really a long way from a language like Java to fully thinking in the way Rust works. In my head, everything still behaves like a Box<dyn Whatever>.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.