fn main() {
let mut a= vec![1,2,3];
a.iter_mut().for_each(|x| *x += 1);
println!("{:?}",a)
}
///////////////////////////////////////
fn main() {
let mut a = vec![1,2,3];
a.iter_mut().map(|x| *x += 1);
println!("{:?}",a)
}
What is the exact difference between these besides map returning the element why map doesnt mutate the variable ?
Would be good if i can get clear explanation. (btw im newbie, came from nodejs)
Based this video both methods are does something to each item.(video has been set to where defines has been made)
Iterators are lazy - they don't do anything unless they're iterated explicitly. map will do its work when the resulting iterator is advanced via next - either directly, or with for loop, or with some consuming method like collect.
In Node.js map is equivalent to Rust's map(…).collect(). Node.js doesn't have a lazy/non-collecting map like Rust does.
You should have gotten a warning during compilation about the map not being used correctly here. Check out whatever console/log/terminal you use to compile Rust.
You use map() when you want to transform each element in a sequence. You rarely for_each() used in real Rust code[1], but it lets you consume the sequence of elements.
As an example, say I want to get a list of Package names. One strategy is to take an iterator of Packages and turn it into an iterator of strings which we can lazily print in a for-loop (the for-loop is the one consuming iterator and where the transform closure will be called). Another strategy is to create an empty Vec and use the for_each() to push each name into it.
struct Package {
name: String,
}
impl Package {
fn new(name: &str) -> Package {
Package {
name: name.to_string(),
}
}
}
fn main() {
let packages: Vec<Package> = vec![
Package::new("first"),
Package::new("second"),
Package::new("third"),
];
println!("Map:");
let names = packages.iter().map(|pkg| &pkg.name);
for name in names {
println!("{name}");
}
let mut names = Vec::new();
packages.iter().for_each(|pkg| names.push(&pkg.name));
println!("For Each: {names:?}");
}
You should read compiler diagnostics carefully. Your code produces the following warning:
Compiling playground v0.0.1 (/playground)
warning: unused `Map` that must be used
--> src/main.rs:3:5
|
3 | a.iter_mut().map(|x| *x += 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_must_use)]` on by default
= note: iterators are lazy and do nothing unless consumed
Which basically explains the exact reason as to why your map example doesn't work. Don't just ignore warnings.
I don't see a good reason to use for_each() other than personal stylistic perference; it's completely equivalent with a for loop over the iterator, except that it's less flexible (because you can e.g. break or return from a loop – you can't do that with the closure in for_each()).
You would use map any time you want to transform an iterator over one type to an iterator over another type. Your usage of trying to mutate inside map is highly non-idiomatic, don't do that.
One reason is that it works by internal iteration. Some iterators like Chain and FlatMap can avoid extra branches when they don't have to re-enter next for every single item in a for loop.
A complete sidenote: there's a proposal to add lazy iterators in ecmascript, with some experimental APIs in Node.js in the stream module. I've been thinking about that proposal about once a week for some time now.
But yes, for any loop body doing material work, one should generally use a normal for loop, not Iterator::for_each. It's there for μoptimization cases and for the occasional times where .for_each(do_the_thing) is tidier after the end of a long chain of things.
Why dont u just use this ? and What transform means exactly? I have understand how map works thanks to @Cerber-Ursi but still couldnt get where to use ?
// let names = packages.iter().map(|pkg| &pkg.name);
let names = packages.iter();
for name in names {
println!("{}",name.name);
}
When I say "transform" I mean "create a new value based on the previous value".
For example, a Redux reducer will transform your app's state from one its previous state to the next state.
Sure you could. That was just a contrived example where I was using map() to convert from an iterator of Packages to an iterator of strings by executing |pkg| &pkg.name on each package.
I'd deliberately split up the bit doing the transformation work (.map(...) in my example, name.name access in yours) from the bit that uses the package names (your for-loop and println!()).
Here is a more realistic example where I iterate over all the keys in a particular resources dictionary, convert them from &str to Text, then for each one I try to look up the "resource value" that corresponds to the key (which may fail), transforming the Text to a Result<(Text, Vector), Error>. Finally, we take this stream of items and collect() it into a Result<OrdMap<Text, Vector<u8>>, crate::Error>.
fn resource_values(
db: &dyn Frontend,
) -> Result<OrdMap<Text, Vector<u8>>, crate::Error> {
let doc = db.parse()?;
doc.resources
.keys()
.map(Text::from)
.map(|name| {
let value = db.resource_value(name.clone())?;
Ok((name, value))
})
.collect()
}
It also makes it easier to see what is happening ("first we turn the keys into Text, then we look up their value") because each operation is in its own map() call instead of all being shoved into the same for-loop body. Chaining on another operation also becomes trivial - just add another map() before we collect the stream of objects into the OrdMap.