Search code examples
rustiterator

Writing a Rust function modifying a struct member in elements of a generic container


Is it possible to write a function which modifies a struct member in place such that one can use it with both Vec and HashMap::values() ?

This is what I came up so far:

use std::collections::HashMap;

struct Foo {
    x: i32,
}

fn process_collection<T>(collection: &mut T)
where
    T: IntoIterator<Item = Foo>,
    for<'a> &'a mut T: IntoIterator<Item = &'a mut Foo>,
{
    for item in collection.into_iter() {
        item.x = 5;
    }
}

fn main() {
    let mut numbers = vec![Foo { x: 1 }, Foo { x: 2 }];
    // Set all .x members to 5.
    process_collection(&mut numbers); 
    
    for item in &numbers {
        println!("item after: {}", item.x);
    }
    
    let mut data: HashMap<String, Foo> = HashMap::new();
    data.insert("One".to_string(), Foo {x: 1} );
    data.insert("Two".to_string(), Foo {x: 2});
    
    // This does not compile.
    process_collection(&mut data.values()); 
}

Solution

  • To modify the elements in place, you only need an iterator with mutable access:

    fn process_collection<'a, T>(collection: T)
    where
        T: IntoIterator<Item = &'a mut Foo>,
    {
        for item in collection {
            item.x = 5;
        }
    }
    

    With which you can call with a Vec<Foo> like this:

    process_collection(&mut numbers);
    

    Or with the values from a HashMap<_, Foo> like this:

    process_collection(data.values_mut()); 
    

    Note the changes:

    • You do not need both T: IntoIterator and &mut T: IntoIterator. Though collections often have both implementations, that is not needed and wouldn't work for the values of a HashMap.
    • This uses values_mut() instead of just values() since the latter only provides immutable references.
    • collection is passed as an owned value and not as a mutable reference since it is more idiomatic.