Как перебирать массив?
Я пытаюсь написать программу, которая включает в себя фильтрацию и складывание по массивам. Я использую Язык программирования ржавчины, первый вариант в качестве ссылки, но я не понимаю, что происходит, когда я формирую итераторы по массивам, Вот пример:
fn compiles() {
let range = (1..6);
let range_iter = range.into_iter();
range_iter.filter(|&x| x == 2);
}
fn does_not_compile() {
let array = [1, 4, 3, 2, 2];
let array_iter = array.into_iter();
//13:34 error: the trait `core::cmp::PartialEq<_>` is not implemented for the type `&_` [E0277]
array_iter.filter(|&x| x == 2);
}
fn janky_workaround() {
let array = [1, 4, 3, 2, 2];
let array_iter = array.into_iter();
// Note the dereference in the lambda body
array_iter.filter(|&x| *x == 2);
}
(площадка для ржавчины)
В первой функции я следую за тем, что итератор по диапазону не переходит в собственность, поэтому я должен взять &x
в filter
лямбда, но я не понимаю, почему второй пример с массивом ведет себя по-разному.
Ответы
Ответ 1
В таких случаях очень полезно заставить компилятор рассказать вам тип переменной. Пусть запускается ошибка типа, назначая аргумент замыкания несовместимому типу:
array_iter.filter(|x| { let () = x; true });
Это не выполняется:
error[E0308]: mismatched types
--> src/main.rs:12:33
|
12 | array_iter.filter(|x| { let () = x; true });
| ^^ expected &&{integer}, found ()
|
= note: expected type `&&{integer}`
found type `()`
Теперь мы знаем, что тип x
- это &&{integer}
- ссылка на ссылку на какой-то целое число. Затем мы можем сопоставить это:
fn hooray() {
let array = [1, 4, 3, 2, 2];
let array_iter = array.into_iter();
array_iter.filter(|&&x| x == 2);
}
Теперь вопрос становится "почему это ссылка на ссылку"? Краткая версия состоит в том, что итератор массива возвращает ссылки (см. Часть type Item = &'a T
). Кроме того, Iterator::filter
передает ссылку закрытию, чтобы предотвратить перемещение и впоследствии потерять не Copy
.
Ответ 2
Массивы - это тип [T; N]
в Rust, для любого типа элемента T
и постоянное число N
. Это массив фиксированного размера.
В настоящий момент Rust не реализует итераторы байтов для массивов. Из-за этого все массивы берутся за кусочки (тип [T]
), и из этого массива доступны методы среза. Массивы также получают итератор среза, который называется std::slice::Iter<'a, T>
и имеет элементы типа &'a T
: он итерации по ссылке!
Вот почему into_iter()
на a Range<i32>
создает итератор i32
и into_iter()
на [i32; 5]
создает итератор &i32
.
Если вам нужны итераторы значений для массивов, они реализованы в более широкой экосистеме, см. (1) и (2).
Ответ 3
Как сказал Shepmaster и bluss, вы можете проверить документацию для типа массива, в которой говорится:
Массивы с размерами от 0 до 32 (включительно) реализуют следующие если тип элемента позволяет это:
-
IntoIterator
(реализовано для &[T; N]
и &mut [T; N]
)
Как говорится, это только для ссылок и отражается в типе Item
: type Item = &'a T
и type Item = &'a mut T
.