Я новичок в Rest и изучаю его на «Rust Essential Training» на Linkedin Learning. В конце главы 5 (Программный поток и управление) я получил следующий вопрос в качестве викторины по главе.
Что не так со следующим кодом для печати каждого элемента
my_array
?fn main() { let my_array = [1, 2, 3]; for element in my_array { println!("element is {}", element); } }
Я не мог придумать в этом ничего плохого, и когда я запустил его в визуальном коде, он заработал без проблем, но они сказали:
Цикл for не может перебирать
my_array
напрямую.Эта программа должна использовать
my_array.iter()
, чтобы получить итератор по массиву.
Может ли кто-нибудь дать мне осмысленное объяснение? Я что-то упустил здесь?
До версии 1.53 это не работало, потому что массивы сами по себе не реализовывали IntoIterator
. Начиная с Rust 1.53, что несколько сбивает с толку, все версии Rust фактически реализуют IntoIterator
для массивов, но:
.into_iter()
для массива эквивалентен прямой итерации (она потребляет).into_iter()
эквивалентно вызову .iter()
(непотребляющая итерация по ссылке) или, точнее, array.into_iter()
разрешается в (&array).into_iter()
, а преобразование ссылки на массив в итератор делает непотребляющий итератор, поддерживаемый массивом.Различие между редакциями связано с «небольшим хаком», который делает это так, хотя массивы реализуют IntoIterator
во всех редакциях по состоянию на 1.53, в 2015 и 2018 годах, компилятор скрывает этот факт исключительно для случая .into_iter()
синтаксиса вызова метода в массивах. , поэтому вместо этого он разрешается в (&array).into_iter()
(что и произошло, когда массивы вообще не реализовывали IntoIterator
). Это хак, потому что он защищает только от синтаксиса вызова метода; любые другие средства повторения массива (например, прямая итерация, iter.zip
, прямой вызов IntoIterator::into_iter
в массиве) найдут это IntoIterator
и переберут его по (потреблению) значению, но ни один из этих методов не работал до 1.53 в любом случае, поэтому нет существующего кода полагался на них (компиляция array.into_iter()
на Rust 2015 или 2018 выдаст громкое предупреждение о том, что значение меняется в Rust 2021).
До версии 1.53 вам нужно было явно преобразовывать в ссылку/срез массива (которые поддерживают итерацию) или явно вызывать .iter()
или .into_iter()
для массива (последний из которых по-прежнему не потребляет, разрешаясь в (&array).into_iter()
; вот почему был необходим хак, поэтому новая функция не изменила поведение существующего кода для 2015 и 2018 годов).
Имейте в виду, что в этом конкретном случае элементы вашего массива реализуют Copy
, поэтому содержимое массива не используется независимо от того, как оно повторяется; единственное различие заключается в том, перебираете ли вы ссылки на целые числа или на целые значения. Но для не-Copy
типов важно различие между потреблением фактических значений и просто «просмотром» по ссылке.
Большое спасибо за ссылку и ваше объяснение.
Я только что заметил, насколько фантастической является «Игровая площадка Rust». Я не могу отблагодарить вас за то, что вы включили это в свой ответ. Я буду часто использовать его для HIR, ASM и MIR. Еще раз спасибо.
@Hanoch: Да, детская площадка довольно волшебная. Пожалуйста!
Все ровно наоборот: начиная с версии Rust 1.53 этот код (прямая итерация) работает всегда. То, что меняется между редакциями, явно into_iter()
: это эквивалентно прямой итерации в редакции 2021 и iter()
в более старых редакциях с использованием вышеупомянутого хака.
@ChayimFriedman: Вы правы, я сначала неправильно прочитал (и теперь переписал).
Это по-прежнему неверно: прямая итерация во всех редакциях потребляется с версии 1.53.0. Какие изменения into_iter()
: это эквивалентно прямой итерации в редакции 2021 и iter()
(не потребляющей) в предыдущих редакциях.
@ChayimFriedman: Хорошо, я перечитал ваши комментарии и объяснение, которое я давал несколько раз, и думаю, что теперь оно верно на 100%. Люди до версии 1.53 часто писали .into_iter()
(хотя это ничего им не давало, кроме .iter()
), и это работало (не потребляя много времени), потому что разрешалось (&array).into_iter()
. Хак в 1.53 существует, чтобы избежать взлома кода до 1.53, заставляя Rust 2015 и 2018 продолжать обрабатывать этот код одинаково, даже несмотря на то, что массивы теперь реализуют IntoIterator
. В Rust 2021 array.into_iter()
разрешается непосредственно в массиве и потребляется. Спасибо за исправления!
Да, не правильно.
Их пример устарел. В более новых версиях Rust массивы можно перебирать напрямую.