Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Rust: how to combine iter(), iter::once(), and iter::empty()?

The following code works:

let a = [1, 2, 3].iter();
let b = iter::once(&456);

for i in a.chain(b) {
    println!("{}", i);
}

Output:

1
2
3
456

But now, I need to change b (depending on some condition) to be either iter() or iter::once , e.g.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

let a = [1, 2, 3].iter();
let b = if some_condition {
    [4, 5, 6].iter()
} else {
    iter::once(&456)
};

for i in a.chain(b) {
    println!("{}", i);
}

But I got:

error[E0308]: `if` and `else` have incompatible types
   |
   |       let b = if some_condition {
   |  _____________-
   | |         [4, 5, 6].iter()
   | |         ---------------- expected because of this
   | |     } else {
   | |         iter::once(&456)
   | |         ^^^^^^^^^^^^^^^^ expected `Iter<'_, {integer}>`, found `Once<&{integer}>`
   | |     };
   | |_____- `if` and `else` have incompatible types
   |
   = note: expected struct `std::slice::Iter<'_, {integer}>`
              found struct `std::iter::Once<&{integer}>`

What should I do?

>Solution :

Because the types of the two iterators are different, you have to type-erase them behind a common abstraction.
Then the correct behaviour is chosen at run-time (dynamic dispatch).

fn main() {
    for attempt in 0..2 {
        println!("~~~~ attempt={} ~~~~", attempt);
        let a = [1, 2, 3].iter();
        let b: Box<dyn Iterator<Item = &i32>> = if attempt == 0 {
            Box::new([4, 5, 6].iter())
        } else {
            Box::new(std::iter::once(&456))
        };
        for i in a.chain(b) {
            println!("{}", i);
        }
    }
}
/*
~~~~ attempt=0 ~~~~
1
2
3
4
5
6
~~~~ attempt=1 ~~~~
1
2
3
456
*/

The previous solution considered the more general case: the two sources of data are very different, but in the end we want to yield &i32.
However, if the problem is really similar to the example in the question, then, we can simply pretend the single value is a slice.

fn main() {
    for attempt in 0..2 {
        println!("~~~~ attempt={} ~~~~", attempt);
        let a = [1, 2, 3].iter();
        let b = if attempt == 0 {
            &[4, 5, 6]
        } else {
            std::slice::from_ref(&456)
        }
        .iter();
        for i in a.chain(b) {
            println!("{}", i);
        }
    }
}
/*
~~~~ attempt=0 ~~~~
1
2
3
4
5
6
~~~~ attempt=1 ~~~~
1
2
3
456
*/
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading