rustrust-itertools

Iterate over two different iterable types with the same item types in a single loop in Rust


In a Rust function, I have two different collection types that both contain strings. I need to iterate over one of them with a loop, but the iterator types are different. How would I bring both iterators to a common denominator?

use std::collections::{HashMap, HashSet};

fn main() {
    run(true);
    run(false);
}

fn run(some_argument: bool) {
    let set: HashSet<String> = (0..4).map(|v| v.to_string()).collect();
    let map: HashMap<String, i32> = (5..9).map(|v| (v.to_string(), v)).collect();

    // this should be a generic non-consuming iterator over &String (or &str?) values
    let iter = if some_argument {
        &set.iter()
    } else {
        &map.keys()
    };

    for v in iter {
        println!("{v}");
    }
}

P.S. Bonus: what if one of the collection types stores &str instead of String - can that be done without significant reallocations/complexities?


Solution

  • There are 2 solutions which can achieve similar functionality to what you are looking for

    1. Using a Box as the iterator
    fn run(some_argument: bool) {
        let set: HashSet<String> = (0..4).map(|v| v.to_string()).collect();
        let map: HashMap<String, i32> = (5..9).map(|v| (v.to_string(), v)).collect();
    
        let iter: Box<dyn Iterator<Item=&String>> = if some_argument {
            Box::new(set.iter())
        } else {
            Box::new(map.keys())
        };
    
        for v in iter {
            println!("{v}");
        }
    }
    
    1. Moving the iterator logic to another function
    fn run(some_argument: bool) {
        let set: HashSet<String> = (0..4).map(|v| v.to_string()).collect();
        let map: HashMap<String, i32> = (5..9).map(|v| (v.to_string(), v)).collect();
    
        if some_argument {
            print(&mut set.iter());
        } else {
            print(&mut map.keys());
        }
    }
    
    fn print(iter: &mut dyn Iterator<Item=&String>) {
        for v in iter {
            println!("{v}");
        }
    }