Search code examples
genericsrustcollections

How to write a generic function over nested collections in Rust?


I wish to write a generic function in Rust that can iterate repeatedly over a collection of elements, which in turn contain a collection that is repeatedly iterated over. The elements are pairs of (Label, Collection<Elem>), where Label is some arbitrary type and Collection is something that can be iterated over multiple times having elements of type Elem. I specifically wish to perform repeated iterations without cloning any of the collections, accidentally or otherwise.

Examples of such collections:

let collection1 = vec![(1u8,vec![0u8,1u8]), (2u8,vec![2u8,3u8,4u8])];
let collection2 = LinkedList::from([("a",[1,2,3]),("b",[4,5,6])]);

An example of a non-generic function that can iterate over collection1 without cloning:

fn nesting(collection : &Vec<(u8,Vec<u8>)>) -> () {
    for (l,c) in collection {
        print!("{}: ",l);
        for e in c.iter() {
            print!("{}, ",e);
        }
        for e in c.iter() {
            print!("{}, ",e);
        }
        println!();
    }
    for (l,_) in collection {
        println!("{:?}",l);
    }
}

One approach I've considered is like this:

fn nesting<'a, L:'a,N:'a,I:Clone+IntoIterator<Item=&'a (L,N)>>(collection: I) -> ()
{ 
  for i in collection.clone() { /* ... */ };
  for i in collection.clone() { /* ... */ };
}

While this can be called on a shared reference to a collection, it can also be called on a collection containing references, in which case the entire collection will be cloned. And what type constraint should N have?


Solution

  • struct Elem;
    fn take_elem(_: &Elem) {}
    
    fn nesting<'a, Label, Outer, Inner>(collection: &'a Outer)
    where
        Inner: 'a,
        Label: 'a,
        &'a Outer: IntoIterator<Item = &'a (Label, Inner)>,
        &'a Inner: IntoIterator<Item = &'a Elem>,
    {
        for (_label, inner) in collection {
            for elem in inner {
                take_elem(elem);
            }
        }
        
        for (_label, inner) in collection {
            for elem in inner {
                take_elem(elem);
            }
        }
    }
    

    This is not the fullest generality possible (for example, it disallows collection that yield owned items with references), but I believe it should suffice for you.