1

In the following snippet, I do not understand why the closure takes its parameter s by reference (&s), then dereferences it (*s):

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...");
    }
}

Am I missing something obvious?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 2
    It would be nice if you were a little more precise on *what* seems confusing to you. The use of `||`, the absence of `return`, the fact that `needle` is not explicitly captured (so how does it whether to capture by reference or value), ... – Matthieu M. Mar 01 '19 at 16:40
  • Sorry: What confuses me is the use of |&s| in the closure parameter, and while also needing to use *s inside the closure. – Pat Shaughnessy Mar 01 '19 at 16:42
  • `find(|&s| s == &needle)` looks fine to me – Boiethios Mar 01 '19 at 16:43
  • Ah interesting I didn't think of that. Thank you. Is &needle what Matthieu meant by capturing by reference? – Pat Shaughnessy Mar 01 '19 at 16:51
  • 2
    I think `if haystack.contains(&needle)` is more idiomatic for `Vec` – Stargateur Mar 01 '19 at 17:11
  • This is **specifically** called out in [the documentation for the method you are using](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.find): *Because `find()` takes a reference, and many iterators iterate over references, this leads to a possibly confusing situation where the argument is a double reference. You can see this effect in the examples below, with `&&x`.* – Shepmaster Mar 01 '19 at 20:02

1 Answers1

4

It is idiomatic. Some people might have different preference for what exactly is dereferenced when, and/or use .as_str() instead of dereferencing, but in general it's OK.


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. Even though that reference of reference isn't ideal in this particular case, .find() is a generic function that has to consistently work with any type, so it just blindly makes a reference.


In arguments &s is the opposite of taking a reference. The closure argument syntax is:

|pattern: type|

and the type is optional. So what you're left with is the pattern.

Your closure is then:

|pattern: &&String|

and with your pattern:

|&s: &&String| 

matches one level of &, so you end up with s being &String.

And then you want to compare s of type &String with a String. To compare equal types, you dereference &String to String with *s.

Kornel
  • 97,764
  • 37
  • 219
  • 309
  • 1
    *&s is the opposite of taking argument by reference* — no, it's not. It still takes an argument by reference, it just immediately dereferences it. `foo(bar: &String)` and `foo(bar: String)` are opposites in this dimension. – Shepmaster Mar 01 '19 at 19:57