Строковое равенство в Rust: как работают ссылки и разыменования? - PullRequest
0 голосов
/ 02 мая 2018

Как новичок в Rust, я работаю над проблемами Project Euler , чтобы помочь мне понять язык. Проблема 4 касается палиндромов, и я нашел два решения для создания вектора палиндромов, но я не уверен, как работает любой из них.

Я использую вектор строк, products, который рассчитывается так:

let mut products = Vec::new();
for i in 100..500 {
    for j in 500..1000 {
        products.push((i * j).to_string());
    }
}

Для фильтрации только тех продуктов, которые являются палиндромными, у меня есть два следующих решения:

Решение 1:

let palindromes: Vec<_> = products
    .iter()
    .filter(|&x| x == &x.chars().rev().collect::<String>())
    .collect();

Решение 2:

let palindromes: Vec<_> = products
    .iter()
    .filter(|&x| *x == *x.chars().rev().collect::<String>())
    .collect();

Они оба дают правильный результат, но я понятия не имею, почему!

В решении 1 мы сравниваем ссылку на строку со ссылкой на только что созданную строку?

В решении 2 мы разыменовываем ссылку на строку и сравниваем ее с новой строкой с разыменовкой?

Что я ожидал бы сделать:

let palindromes: Vec<_> = products
    .iter()
    .filter(|x| x == x.chars().rev().collect::<String>())
    .collect();

Я надеюсь, что кто-нибудь сможет мне объяснить:

  • В чем разница между моими двумя решениями и почему они оба работают?
  • Почему я не могу просто использовать x без ссылки или разыменования его в функции фильтра?

Спасибо!

1 Ответ

0 голосов
/ 02 мая 2018
  1. Vec<String>.iter() возвращает итератор по ссылкам (&String).
  2. Аргумент замыкания .filter() принимает ссылку на элемент итератора. Таким образом, тип, который передается в замыкание, является двойной ссылкой &&String.
  3. |&x| говорит закрытию ожидать ссылку, поэтому x теперь имеет тип &String.

Первое решение: collect возвращает String, из которых & принимает ссылку. x также является ссылкой на строку, поэтому сравнение выполняется между двумя &String.

Второе решение: оператор разыменования * применяется к x, что приводит к String. Правая сторона интересна: результат String для collect разыменовывается. Это приводит к срезу строки, потому что String реализует Deref<Target=str>. Теперь сравнение выполняется между String и str, что работает, поскольку оно реализовано в стандартной библиотеке (обратите внимание, что a == b эквивалентно a.eq(&b)).

Третье решение: компилятор объясняет, почему он не работает.

черта std::cmp::PartialEq<std::string::String> не реализована для &&std::string::String

Левая сторона - это двойная ссылка на строку (&&String), а правая - просто String. Вам нужно вывести обе стороны на один и тот же «контрольный уровень». Все эти работы:

x.iter().filter(|x| x == &&x.chars().rev().collect::<String>());
x.iter().filter(|x| *x == &x.chars().rev().collect::<String>());
x.iter().filter(|x| **x == x.chars().rev().collect::<String>());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...