Почему `find` берет параметр по ссылке, а затем разыменовывает его?

В следующем фрагменте я не понимаю, почему замыкание берет свой параметр s по ссылке (&s), а затем разыменовывает его (*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...");
    }
}

Я упускаю что-то очевидное?


person Pat Shaughnessy    schedule 01.03.2019    source источник
comment
Было бы неплохо, если бы вы уточнили, что вас смущает. Использование ||, отсутствие return, тот факт, что needle не захватывается явно (так как же захватывать по ссылке или по значению),...   -  person Matthieu M.    schedule 01.03.2019
comment
Извините: меня смущает использование |&s| в параметре замыкания, а также необходимо использовать *s внутри замыкания.   -  person Pat Shaughnessy    schedule 01.03.2019
comment
find(|&s| s == &needle) выглядит хорошо для меня   -  person Boiethios    schedule 01.03.2019
comment
О, интересно, я не подумал об этом. Спасибо. Является ли &needle тем, что Матье имел в виду, когда говорил о референции?   -  person Pat Shaughnessy    schedule 01.03.2019
comment
Я думаю, что if haystack.contains(&needle) более идиоматично для Vec   -  person Stargateur    schedule 01.03.2019
comment
Это конкретно указано в документация для используемого вами метода: Поскольку find() принимает ссылку, а многие итераторы перебирают ссылки, это может привести к запутанной ситуации, когда аргумент является двойной ссылкой. Вы можете увидеть этот эффект в приведенных ниже примерах с &&x.   -  person Shepmaster    schedule 01.03.2019


Ответы (1)


Это идиоматично. У некоторых людей могут быть другие предпочтения в отношении того, что именно разыменовывается, и/или использовать .as_str() вместо разыменования, но в целом это нормально.


В вашем случае .iter() перебирает Strings по ссылке, поэтому он дает &String элементов.

Затем .find() дает вам доступ к каждому итерированному элементу по ссылке снова, поэтому вы получаете аргумент &&String. Несмотря на то, что эта ссылка на ссылку не идеальна в данном конкретном случае, .find() — это общая функция, которая должна последовательно работать с любым типом, поэтому она просто делает ссылку вслепую.


В аргументах &s является противоположным взятию ссылки. Синтаксис аргумента закрытия:

|pattern: type|

и тип является необязательным. Итак, у вас остался шаблон.

Тогда ваше закрытие:

|pattern: &&String|

и с вашим шаблоном:

|&s: &&String| 

соответствует одному уровню &, так что в итоге s будет &String.

И затем вы хотите сравнить s типа &String с типом String. Чтобы сравнить одинаковые типы, вы разыменовываете &String в String с *s.

person Kornel    schedule 01.03.2019
comment
&s противоположен приему аргумента по ссылке — нет, это не так. Он по-прежнему принимает аргумент по ссылке, просто сразу же разыменовывает его. foo(bar: &String) и foo(bar: String) являются противоположностями в этом измерении. - person Shepmaster; 01.03.2019