Search code examples
rustiteratormutable

Rust function that takes an iterator, and returns an iterator with mutated items?


I want to write a function that takes one iterator as input, and return an iterator with the same items but mutated. Like in this example:

fn greet(name: &mut String) {
    name.insert_str(0, "Hello ");
}

fn to_greetings(names: impl Iterator<Item = String>) -> impl Iterator<Item = String> {
    names.inspect(|name| greet(name))
}

This does not compile, since name isn't mutable. I have tried adding mut in various places without really understanding what I am doing, but without any success. How do I fix the above code?

I guess I need to make the items mutable, but apparently Item = mut String is a syntax error.


Solution

  • If the item type of an iterator is String, the iterator yields owned strings. The iterator transfers ownership of these strings to the consumer. The consumer can modify the strings, since it owns them.

    However, you can't use inspect() to modify the elements of an iterator. The closure passed to inspect() receives an immutable reference to the items of the iterator, which can't be used to modify the items. The correct iterator adapter to modify items is map(), so this works (but it doesn't feel particularly idiomatic to me):

    fn to_greetings<I>(names: I) -> impl Iterator<Item = String>
    where
        I: Iterator<Item = String>,
    {
        names.map(|mut name| { greet(&mut name); name })
    }
    

    If you want to actually modify the strings in some underlying container, e.g. a vector of strings, you need a different approach though. A mutable iterator over a container of strings has an item type of &mut String, so you'd need to use something like

    fn to_greetings<'a, I>(names: I) -> impl Iterator<Item = &'a mut String>
    where
        I: Iterator<Item = &'a mut String>,
    {
        names.map(|name| { greet(name); name })
    }