What's the purpose of ".map(|&x| x)" in Rust - rust

I'm learning Rust and noticed the following iterator pattern in a number of places:
let some_vector: &[& str] = &["hello", "world", "zombies", "pants"];
let result: Vec<&str> = some_vector
.iter()
.filter(|&x| *x == "hello")
.map(|&x| x)
.collect();
What's the purpose of that .map(|&x| x)? Why is it necessary? Does it create a copy?
When I remove it, I get the following compiler error:
error[E0277]: a value of type `Vec<&str>` cannot be built from an iterator over elements of type `&&str`
--> src/main.rs:7:6
|
7 | .collect();
| ^^^^^^^ value of type `Vec<&str>` cannot be built from `std::iter::Iterator<Item=&&str>`
|
= help: the trait `FromIterator<&&str>` is not implemented for `Vec<&str>`
note: required by a bound in `collect`
For more information about this error, try `rustc --explain E0277`.
So the map turns an iterator over references to string slices into an iterator over string slices? Removing one level of indirection? Is that right?

In addition to #AlexW's answer, actually there is no need to write that, because there is a builtin iterator adapter that does it better (more clear, more performant): copied().
let some_vector: &[&str] = &["hello", "world", "zombies", "pants"];
let result: Vec<&str> = some_vector
.iter()
.filter(|&x| *x == "hello")
.copied()
.collect();
There is also cloned() which is equal to .map(|x| x.clone()).

Assuming you're using 2021 edition, it converts from impl Iterator< Item = &&str> to impl Iterator< Item = &str>:
let some_vector: &[& str] = &["hello", "world", "zombies", "pants"];
let result: Vec<&str> = some_vector // &[&str]
.iter() // Iter<&str>
.filter(|&x| *x == "hello") // Impl Iterator< Item = &&str>
.map(|&x| x) // Impl Iterator< Item = &str>
.collect();
And the reason it's necessary is because the FromIterator trait is already implemented for &str as it's a relatively more common use case and it's not implemented for &&str as the error message says:
the trait `FromIterator<&&str>` is not implemented for `Vec<&str>`

Related

Why does a variable holding the result of Vec::get_mut not need to be mutable?

I have the following code:
fn main() {
let mut vec = Vec::new();
vec.push(String::from("Foo"));
let mut row = vec.get_mut(0).unwrap();
row.push('!');
println!("{}", vec[0])
}
It prints out "Foo!", but the compiler tells me:
warning: variable does not need to be mutable
--> src/main.rs:4:9
|
4 | let mut row = vec.get_mut(0).unwrap();
| ----^^^
| |
| help: remove this `mut`
Surprisingly, removing the mut works. This raises a few questions:
Why does this work?
Why doesn't this work when I use vec.get instead of vec.get_mut, regardless of whether I use let or let mut?
Why doesn't vec work in the same way, i.e. when I use let vec = Vec::new(), why can't I call vec.push()?
vec.get_mut(0) returns an Option<&mut String>, so when you unwrap that value you will have a mutable borrow of a String. Remember, that a let statement's left side is using pattern matching, so when your pattern is just a variable name you essentially say match whatever is on the right and call it name. Thus row matches against &mut String so it already is mutable.
Here's a much simpler and more straightforward example to illustrate the case (which you can try in the playground):
fn main() {
let mut x = 55i32;
dbg!(&x);
let y = &mut x; // <-- y's type is `&mut i32`
*y = 12;
dbg!(&x);
}

Finding most frequently occurring string in a structure in Rust?

I'm looking for the string which occurs most frequently in the second part of the tuple of Vec<(String, Vec<String>)>:
use itertools::Itertools; // 0.8.0
fn main() {
let edges: Vec<(String, Vec<String>)> = vec![];
let x = edges
.iter()
.flat_map(|x| &x.1)
.map(|x| &x[..])
.sorted()
.group_by(|x| x)
.max_by_key(|x| x.len());
}
Playground
This:
takes the iterator
flat-maps to the second part of the tuple
turns elements into a &str
sorts it (via itertools)
groups it by string (via itertools)
find the group with the highest count
This supposedly gives me the group with the most frequently occurring string, except it doesn't compile:
error[E0599]: no method named `max_by_key` found for type `itertools::groupbylazy::GroupBy<&&str, std::vec::IntoIter<&str>, [closure#src/lib.rs:9:19: 9:24]>` in the current scope
--> src/lib.rs:10:10
|
10 | .max_by_key(|x| x.len());
| ^^^^^^^^^^
|
= note: the method `max_by_key` exists but the following trait bounds were not satisfied:
`&mut itertools::groupbylazy::GroupBy<&&str, std::vec::IntoIter<&str>, [closure#src/lib.rs:9:19: 9:24]> : std::iter::Iterator`
I'm totally lost in these types.
You didn't read the documentation for a function you are using. This is not a good idea.
This type implements IntoIterator (it is not an iterator itself),
because the group iterators need to borrow from this value. It should
be stored in a local variable or temporary and iterated.
Personally, I'd just use a BTreeMap or HashMap:
let mut counts = BTreeMap::new();
for word in edges.iter().flat_map(|x| &x.1) {
*counts.entry(word).or_insert(0) += 1;
}
let max = counts.into_iter().max_by_key(|&(_, count)| count);
println!("{:?}", max);
If you really wanted to use the iterators, it could look something like this:
let groups = edges
.iter()
.flat_map(|x| &x.1)
.sorted()
.group_by(|&x| x);
let max = groups
.into_iter()
.map(|(key, group)| (key, group.count()))
.max_by_key(|&(_, count)| count);

Slice and iter() simultaneously

I am trying to figure out why this does not work (Playground):
fn main() {
let a = vec![1, 2, 3, 4];
let b = a.clone();
// slice and iter (wrong way)
let s: i32 = &a[1..a.len()].iter()
.zip(&b[1..b.len()].iter())
.map(|(x, y)| x * y)
.sum();
println!("{}", s);
}
Error:
rustc 1.13.0 (2c6933acc 2016-11-07)
error[E0277]: the trait bound `&std::slice::Iter<'_, {integer}>: std::iter::Iterator` is not satisfied
--> <anon>:6:10
|
6 | .zip(&b[1..b.len()].iter())
| ^^^ trait `&std::slice::Iter<'_, {integer}>: std::iter::Iterator` not satisfied
|
= note: `&std::slice::Iter<'_, {integer}>` is not an iterator; maybe try calling `.iter()` or a similar method
= note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&std::slice::Iter<'_, {integer}>`
error: no method named `map` found for type `std::iter::Zip<std::slice::Iter<'_, {integer}>, &std::slice::Iter<'_, {integer}>>` in the current scope
--> <anon>:7:10
|
7 | .map(|(x, y)| x * y)
| ^^^
|
= note: the method `map` exists but the following trait bounds were not satisfied: `&std::slice::Iter<'_, {integer}> : std::iter::Iterator`, `std::iter::Zip<std::slice::Iter<'_, {integer}>, &std::slice::Iter<'_, {integer}>> : std::iter::Iterator`
But this does work:
fn main() {
let a = vec![1, 2, 3, 4];
let b = a.clone();
// slice and iter (correct way)
let s: i32 = a[1..a.len()].iter()
.zip(b[1..b.len()].iter())
.map(|(x, y)| x * y)
.sum();
println!("{}", s);
}
Please explain how vectors work in Rust and the difference above when I iter().
In short: you probably misunderstood operator precedence:
&b[1..b.len()].iter()
Is equal to:
&(b[1..b.len()].iter())
And since zip() is expecting something that implements IntoIterator, the call fails, since a reference to this iterator type does not implement said trait.
Full Explanation
Let's try to understand the error message! Of course, we will first just look at the first error:
error[E0277]: the trait bound `&std::slice::Iter<'_, {integer}>: std::iter::Iterator` is not satisfied
--> <anon>:6:10
|
6 | .zip(&b[1..b.len()].iter())
| ^^^ trait `&std::slice::Iter<'_, {integer}>: std::iter::Iterator` not satisfied
|
= note: `&std::slice::Iter<'_, {integer}>` is not an iterator; maybe try calling `.iter()` or a similar method
= note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&std::slice::Iter<'_, {integer}>`
Wow, that's quite a mouthful. But we can see that some trait bound requirement of the function zip() is violated. So, let's look at the signature of said function:
fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter>
where U: IntoIterator
What matters is the other argument (type U). U has to be IntoIterator. This trait is implemented for quite a few types ... let's check what type we're trying to pass into zip():
&b[1..b.len()].iter()
To analyze this completely, we need to understand quite something, but I'll try to break it down. First, let's disambiguate operator precedence by inserting more parenthesis. The above code snippet is equivalent to:
&(b[1..b.len()].iter())
An expression foo[bar] desugares to *::std::ops::Index::index(&foo, bar). This is the most complex part here, but looking this up in the documentation reveals that the expression b[1..b.len()] has the type [i32].
On that type, you call iter() which returns a type Iter<_, _> which is the iterator type for slices.
Now the&: you borrow this Iter<_, _> thing, resulting in &Iter<_, _>.
And hey, this matches the error message! Look at the last note:
note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&std::slice::Iter<'_, {integer}>`
So... what does satisfy the IntoIterator trait? For one, every type that implements Iterator (e.g. Iter<_, _>) also implements IntoIterator. So you can just remove the & in the expression and it works!
But we can do even better! IntoIterator is also implemented for &[T], so you can also just remove the .iter() and it works!
Working Code
let s: i32 = a[1..].iter()
.zip(&b[1..])
.map(|(x, y)| x * y)
.sum();
Note: I also removed the ranges' upper bounds to make them half open, as Paolo Falabella suggested.
Your first version has an issue with operator precedence: &a[1..a.len()].iter() applies iter() first and then takes a reference to it, ending with a reference to a std::slice::Iter.
As you can see on the docs for Iter , there is an impl Iterator for Iter but not for &Iter.
This is what the first error is trying to say: (look at the part that says: &std::slice::Iter<'_, {integer}> is not an iterator).
Simplifying a bit, you can have:
fn main() {
let a = vec![1, 2, 3, 4];
// let b = a.clone(); // no more need to clone. We're going to only
// work with references
let s: i32 = (&a[1..]).iter() // you don't need the a.len()
// to slice to the end
.zip(&a[1..]) // &a implements IntoIter, which zip
// accepts, so you don't need iter()
.map(|(x, y)| x * y)
.sum();
println!("{}", s);
}
Iterator::zip expects something that implements IntoIterator.
Instead of passing an Iterator, you're passing a reference to the Iterator. Iterators mutate, and a reference isn't sufficient.
You can resolve this by using parenthesis to make it clear what you're trying to grab a reference from
fn main() {
let a = vec![1, 2, 3, 4];
let b = a.clone();
let s: i32 = (&a)[1..a.len()].iter()
.zip(((&b)[1..b.len()]).iter())
.map(|(x, y)| x * y)
.sum();
println!("{}", s);
}

Chunking a vector of strings

I'm trying to chunk an vector of uneven length strings into a vector of even length strings. The laziest way I could think of doing this is to join the arguments into a string, convert the chars to a vector, and then use Vec::chunks. Unfortunately, I'm running into issues trying to collect the chunks into strings.
let args: Vec<String> = ["123", "4", "56"].iter().map(|&s| s.into()).collect();
let result: Vec<String> = args
.join(" ")
.chars()
.collect::<Vec<_>>()
.chunks(2)
.map(|c| c.collect::<String>())
.collect::<Vec<String>>();
assert_eq!(["12", "34", "56"], result);
Results in the error:
error[E0599]: no method named `collect` found for type `&[char]` in the current scope
--> src/main.rs:9:20
|
9 | .map(|c| c.collect::<String>())
| ^^^^^^^
|
= note: the method `collect` exists but the following trait bounds were not satisfied:
`&mut &[char] : std::iter::Iterator`
`&mut [char] : std::iter::Iterator`
You weren't far off:
let result: Vec<String> = args
.join("")
.chars()
.collect::<Vec<_>>()
.chunks(2)
.map(|x| x.iter().cloned().collect())
.collect();
println!("{:?}", result);
You probably don't want a space when joining them together.
You need to convert each chunk (which is a &[char]) into an iterator via .iter(). You then have to convert the iterated type from a &char to a char via .cloned().
I might write this using Itertools::chunks though:
use itertools::Itertools; // 0.8.0
fn main() {
let args = ["123", "4", "56"];
let together = args.iter().flat_map(|x| x.chars());
let result: Vec<String> = together
.chunks(2)
.into_iter()
.map(|x| x.collect())
.collect();
println!("{:?}", result);
}
flat_map avoids the need to create a String, it just chains one iterator to the next.
Itertools::chunks allows the programmer to not create an intermediate Vec. Instead, it has an internal vector that, IIRC, will only store up to n values in it before yielding a value. This way you are buffering a smaller amount of items.

What is a clean way to convert a Result into an Option?

Before updating to a more recent Rust version the following used to work:
fn example(val: &[&str]) {
let parsed_value: Vec<usize> = val
.iter()
.filter_map(|e| e.parse::<usize>())
.collect();
}
However, now the parse method returns a Result type instead of an Option and I get the error:
error[E0308]: mismatched types
--> src/lib.rs:4:25
|
4 | .filter_map(|e| e.parse::<usize>())
| ^^^^^^^^^^^^^^^^^^ expected enum `std::option::Option`, found enum `std::result::Result`
|
= note: expected type `std::option::Option<_>`
found type `std::result::Result<usize, std::num::ParseIntError>`
I could create an Option through a conditional, but is there a better / cleaner way?
Use Result::ok. Types added for clarity:
let res: Result<u8, ()> = Ok(42);
let opt: Option<u8> = res.ok();
println!("{:?}", opt);
For symmetry's sake, there's also Option::ok_or and Option::ok_or_else to go from an Option to a Result.
In your case, you have an iterator.
If you'd like to ignore failures, use Iterator::flat_map. Since Result (and Option) implement IntoIterator, this works:
let parsed_value: Vec<usize> = val
.iter()
.flat_map(|e| e.parse())
.collect();
If you'd like to stop on the first failure, you can collect into one big Result. This is less obvious, but you can check out the implementors of FromIterator for the full list of collect-able items.
let parsed_value: Result<Vec<usize>, _> = val
.iter()
.map(|e| e.parse())
.collect();
Of course, you can then convert the one big Result into an Option, as the first example shows.

Resources