Sorry if this is a n naïve question. I'm only just learning Rust.
In the code snippets below, WORDS is a const string literal containing a long list of whitespace separated words. To collect these words into a Vec<String> the following works just fine:
let mut vec_words = Vec::new();
for word in WORDS.split_whitespace() {
vec_words.push(word.to_string());
}
But this doesn't and I don't see any way to make it work with .to_string(), to.owned() or similar:
let vec_words: Vec<String> = WORDS.split_whitespace().collect();
it gives the error message:
error[E0277]: a value of type `Vec<String>` cannot be built from an iterator over elements of type `&str`
--> src/main.rs:15:57
|
15 | let vec_words: Vec<String> = WORDS.split_whitespace().collect();
| ^^^^^^^ value of type `Vec<String>` cannot be built from `std::iter::Iterator<Item=&str>`
|
= help: the trait `FromIterator<&str>` is not implemented for `Vec<String>`
Is there any way to achieve this without a for loop, as iterator methods are shorter, more readable, and as I understand it, more idiomatic and (at least sometimes) more efficient?
In case anyone is curious, the words also needs to be filtered through a validation function which checks that the length of the words is as required, among various other things. So, the final version with iterator methods looks like this:
let vec_words: Vec<String> = WORDS
.split_whitespace()
.filter(|word| validate(word, word_len))
.map(|word| word.to_owned())
.collect();
As opposed to the for loop version:
let mut vec_words = Vec::new();
for word in WORDS.split_whitespace() {
if validate(word, word_len) {
vec_words.push(word.to_string());
}
}
Not really much difference in length of code, but it's nice to stick to the more idiomatic form.
Actually, I'm not quite sure. I do now that these only work with trait methods. It's allowed somehow between all these more commonly-known things like str::to_owned(...) or ToOwned::to_owned or <str>::to_owned(...) or <str as ToOwned>::to_owned. This last one also supports <_ as ToOwned>::to_owned; the trait can also have parameters elided, e.g. let x: &str = <_ as AsRef<_>>::as_ref(String::new());, perhaps somewhere in all of this system, <_>::to_owned and the like became allowed, too. This is somewhat sparsely documented (if at all), so I'm not even quite sure whether this is 100% an intentional language feature, but it works so it's stable.
Here's an interesting test case...
trait Foo {
fn foo(&self);
}
trait Foo2 {
fn foo(&self);
}
struct Bar;
impl Foo for Bar {
fn foo(&self) {}
}
fn test(x: Bar) {
x.foo(); // <- works
<_>::foo(&x); // <- doesn't work ("multiple applicable items in scope")
}
apparently it's less smart than method resolution. But that kind-of makes sense if this is a corner-case of the disambiguation syntax; if it's disambiguation syntax, it won't resolve the ambiguities for you. By this test, it seems like it's considering all the traits in scope (simple to test that traits not in scope don't apply) and all their methods, and if there's only a single one matching the name, then it works.
Yup -- the same way that you can't call trait methods from traits that aren't in scope.
Basically it's the same as the trait part of normal method resolution, just having skipped the inherent method part. That's the same as what happens during inference on something that's still an unknown inference variable -- you can call trait methods, but it won't look at inherent methods because that would make any conflicting inherent method anywhere an inference-breaking change.
It's not really a "shortcut", though. It basically falls straight out of the uniformity of the language: methods (inherent as well as trait) are just functions. Wherever a function-like value is expected, you can use them. There's not much special about closures (from this point of view, at least), so you usually don't need a closure if you already have a matching free function or method with the same signature.