Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create Vec<String> from literal

Tags:

rust

I want to do

fn main() {
    let options: Vec<String> = vec!["a", "b", "c"].map(|s| s.to_owned()).collect();
}

because this seems like the easiest way to get a vector of owned Strings, but I get hit with this error:

error: no method named `map` found for type `std::vec::Vec<&str>` in the current scope
...
note: the method `map` exists but the following trait bounds were not satisfied:
`std::vec::Vec<&str> : std::iter::Iterator`, `[&str] : std::iter::Iterator`

I don't see where the need for the [&str] : std::iter::Iterator bound comes from. if you ignore the part with split_whitespace I'm basically doing what answers on this question recommend.

How should I be generating this vector?

like image 870
turbulencetoo Avatar asked Mar 30 '17 22:03

turbulencetoo


1 Answers

if you ignore the part with split_whitespace

Yes, except you cannot ignore this part. The docs for split_whitespace state (emphasis mine):

The iterator returned

split_whitespace returns an iterator over the pieces of the string that were separated by whitespace, and map is a method on Iterator.

A Vec is not an iterator. You can see that Vec does not implement it's own map method:

no method named map found for type std::vec::Vec<&str>

And the compiler tries to suggest what you might have meant, but weren't quite achieving:

note: the method map exists but the following trait bounds were not satisfied:

You can get an iterator from a Vec by calling Vec::iter or into_iter:

fn main() {
    let options: Vec<String> = vec!["a", "b", "c"].into_iter().map(|s| s.to_owned()).collect();
}

However, there's no need to allocate two vectors here, an array and a vector is more efficient:

let options: Vec<_> = ["a", "b", "c"].iter().map(|s| s.to_string()).collect();

An iterator over a slice returns references (&T) to the elements in the slice. Since each element is already a &str, the type of s is a &&str. Calling to_owned on a reference to a reference simply clones the reference. You could also have said .map(|&s| s.to_owned()), which dereferences the value once, producing a &str. Calling to_owned on a &str allocates a String.

like image 178
Shepmaster Avatar answered Oct 21 '22 08:10

Shepmaster