For .. in .. vs iter().map of vec!

I've the below code that works fine:

let users = vec!("wrong@email", "correct@gmail.com");

for user in users {
            match template.clone().to(user).build() {
                   Err(e) => println!("error: {:?} in email: {}", e, user),
                   Ok(e) => {
                         match mailer.send(e.into()) {
                                 Err(e) => println!("error: {:?} in emailing: {}", e, user),
                                 Ok(r) => println!("Emailing: {}, {:?}", user, r.message)
                          }
                  }
         }
}

I'm trying to replace the

for user in users { 
}

By:

users.iter().map(|user| 
)

But found that the iter().map is returning user as &&str which is not accepted fortemplate.clone().to(user).build()

How can I fix it?

The direct translation of for user in users {} is users.into_iter().for_each(|user| {}).
You could also deref user:

template.clone().to(*user).build()

You might have to borrow it again (&**user), I'm not sure.

Both:

users.iter().map(|user|
    match template.clone().to(*user).build() {}
);

And

users.into_iter().for_each(|user| 
     match template.clone().to(user).build() {}
);

Had been worked.

But in both cases I got the below warning:

warning: unused `std::iter::Map` that must be used
  --> src/main.rs:30:25
   |
30 | /                         users.iter().map(|user| {
31 | |                             match template.clone().to(*user).build() {
32 | |                                 Err(e) => println!("error: {:?} in email: {}", e, user),
33 | |                                 Ok(e) => {
...  |
40 | |                         }
41 | |                         );
   | |__________________________^
   |
   = note: #[warn(unused_must_use)] on by default
   = note: iterators are lazy and do nothing unless consumed

Iterators in Rust are lazy, they only do something when you ask them to.
One way is to use for_each.
map is probably not doing what you expect, in this case the entire iterator is created but never "executed".

3 Likes

Sorry if this is a silly Rust newbie question but why would one want to replace something short, sweet simple and obvious:

for user in users 

With something longer, more complex and obscure?

2 Likes

For some iterators using the for_each combinator will result in more efficient code. For example, the Chain and Flatten iterator combintors can be made more efficient.

3 Likes

While true, this is the kind of "optimization" that is heavily context-dependent and can easily turn into a pessimization if over-generalized. If you're using for_each for this kind of optimization advantage, you should definitely be profiling your code and therefore you'll know whether it makes a difference or not.

Personally I have never found a use for for_each -- I'm glad it exists, but I find for i in foo to be more clear, and I've never run across one of those situations where a for loop is slower. But I don't write much perf-sensitive code.

To editorialize a bit, the tendency of some people in Rust to coalesce everything into a chain of iterator adapters reminds me of the tendency in Python to coalesce everything into a nested list comprehension. It feels smart and efficient, and I've done it myself on occasion, but it's possible to go too far. Sometimes a for loop should just be a for loop.

3 Likes

Note that for_each isn't going to be slower, because its default implementation does the same thing a for loop does: call next until it returns None.

That said, I agree that the above still doesn't mean you should default to using for_each over a normal for loop. Sure, if the body is trivial so the for_each looks fine, then go for it if you like how it looks. But if the body is big enough that it looks bad in a for_each, then it's probably complex enough that any optimization difference will be irrelevant anyway.

2 Likes

If the closure is large enough, it may fall foul of LLVM's inlining heuristics and incur function call overhead, while the loop body is "pre-inlined". Actually I really don't know, maybe that never happens in this case, but that's what I was thinking of, anyway.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.