-
-
Save patshaughnessy/3252e2e718445d991499e5cd08e1949c to your computer and use it in GitHub Desktop.
fn main() { | |
let needle = "list".to_string(); | |
let haystack = ["some".to_string(), "long".to_string(), "list".to_string(), "of".to_string(), "strings".to_string()].to_vec(); | |
if let Some(str) = haystack.iter().find(|&s| *s == needle) { | |
println!("{}", needle); | |
} else { | |
println!("Nothing there..."); | |
} | |
} |
I don't have an answer for you, but I am also interested. I wish there was a watch button on these gist's. Being new to Rust I find your solution here very helpful whether its idiomatic or not.
In my search for an answer I found your Stack Overflow question, which seems to already have a pretty good answer.
A key line is "In your case .iter()
iterates over Strings by reference, so it gives &String
elements. Then .find()
gives you access to each iterated the element by reference again, so you end up with a &&String
argument." This is why you've got that |&s| *s ==
mess in your version — I agree that that syntax is confusing!
I'm still learning Rust, and coming from Ruby things like String
vs &str
and references in general still confuse me. But here's how I'd go about this.
I was able to avoid that weird syntax by using contains
. Not sure if it's any more or less idiomatic of Rust (I'm still learning), but it seems tolerable?
fn main() {
let needle: String = "list".to_string();
let haystack: Vec<String> = vec!["some".to_string(), "long".to_string(), "list".to_string(), "of".to_string(), "strings".to_string()];
if haystack.contains(&needle) {
println!("{}", needle);
} else {
println!("not found");
}
}
EDIT: Though as this tweet points out, into_iter
helps simplify your original version a bit:
fn main() {
let needle = "list".to_string();
let haystack = vec!["some".to_string(), "long".to_string(), "list".to_string(), "of".to_string(), "strings".to_string()];
if let Some(word) = haystack.into_iter().find(|s| s == &needle) {
println!("{}", word);
} else {
println!("Nothing there...");
}
}
(I'd avoid naming a variable str
cuz that's just confusing.)
contains
is definitely the idiomatic approach. Also, you can avoid repeating .to_string()
, and you can rely more on type inference:
fn main() {
let needle = "list".into();
let haystack: Vec<_> = vec!["some", "long", "list", "of", "strings"]
.into_iter()
.map(String::from)
.collect();
if haystack.contains(&needle) {
println!("{}", needle);
} else {
println!("not found");
}
}
And if you're going to do membership tests often, consider a set rather than a vector.
Thanks all of you for the ideas!
Yup as I learned on StackOverflow, using contains
seems a lot simpler and cleaner in this example. Using into_iter
will also work, but isn't what I need in my app because I don't want to move the data out.
And thanks for the type inference tip - yes much cleaner.
The deeper issue for me is that Rust's &
and &&
syntax can be very confusing, especially with iterators and closures. Will just take some getting used to I suppose :)
Is this the idiomatic way of finding a String object in a Vec? Am I missing something obvious? the closure syntax
find(|&s| *s == needle)
seems confusing to me.