Ruby ".each loop" to Rust

I am trying to write this line in ruby to rust:

untagged.each {|ins| message += "    #{ins}\n"}

My attempt in rust:

untagged.iter().map(|ins| message += format!("    {}\n", ins).as_str()); 

The code compiles, but I get the following warnings:

unused `std::iter::Map` that must be used
   --> src/main.rs:232:9
    |
232 |         untagged.iter().map(|ins| message += format!("    {}\n", ins).as_str()); 
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `#[warn(unused_must_use)]` on by default
    = note: iterators are lazy and do nothing unless consumed

The first warning says that std::iter::Map needs to be used, but I've already typed the use statement at the top. I'm confused why it isn't being "used". Second, I understand that iterators are lazy, so they don't do anything until I call a method to use them, but is that necessary if all I want to do is add some information to the String message?
Thanks for your time and help.

You probably want untagged.iter().for_each(|ins| messages += format!(" {}\n", ins).as_str()). map creates a new, lazy iterator so that you can chain it with additional combinators; for_each is eager and doesn't return anything, which is what you want here.

7 Likes

The map() method transforms your iterator into a new iterator, but that new iterator doesn't do anything until it's consumed, so the side-effect of appending to message never happens; the whole constructed iterator just gets thrown away by the ; at the end.

That's why for_each() is the correct approach, because it calls a closure on each item and consumes the iterator in the process.

5 Likes

When the compiler says "used" it means you should assign the value to a variable and do something with that variable (e.g. loop over each element in the iterator).

2 Likes

Rust also has a built-in for loop that runs a block of code on each element of an iterator:

for ins in &untagged {
    message += format!("    {}\n", ins).as_str()); 
}
6 Likes

You can avoid the allocation done by format! with writeln!(&mut messages, ...).

use std::fmt::Write;

fn main() {
    let mut messages = String::with_capacity(512);
    let items = [1, 2, 3, 4];

    for item in items {
        writeln!(&mut messages, "    {}", item).unwrap();
    }

    print!("{}", messages);
}
2 Likes