Трейт Iterator
next и ленивые вычисления
«Трейт Iterator» — бесплатный урок Learn Rust Coding на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Learn Rust Coding, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Learn Rust Coding содержит 4 уроков всего.
Что такое итератор
Итератор — это всё, что выдаёт последовательность значений по одному. В Rust итераторы — идиоматичный способ обрабатывать коллекции, такие как векторы, диапазоны и срезы.
Вся концепция построена на одном трейте: Iterator. Как только тип реализует его, Вы получаете десятки полезных методов бесплатно.
Трейт Iterator
Трейт Iterator требует реализации одного метода — next — и одного ассоциированного типа — Item:
type Item— тип выдаваемого значенияfn next(&mut self) -> Option<Self::Item>— возвращает следующее значение
Пока остаются значения, он возвращает Some(value), а после завершения — None.
fn main() {
let v = vec![10, 20, 30];
let mut it = v.iter();
println!("{:?}", it.next());
println!("{:?}", it.next());
println!("{:?}", it.next());
println!("{:?}", it.next());
}next возвращает Option
Каждый вызов next продвигает итератор и передаёт Вам Option. Some оборачивает настоящее значение, а None обозначает конец.
Обратите внимание: next принимает &mut self — вызов изменяет внутреннее состояние, поэтому следующий вызов возвращает другое значение.
fn main() {
let mut it = (1..4).into_iter();
while let Some(n) = it.next() {
println!("got {}", n);
}
println!("done");
}Итераторы ленивы
Само создание итератора не делает ничего. Значения не вычисляются, пока кто-то не запросит их. Это называется ленивостью.
В следующем примере замыкание map не запускается, потому что мы не потребляем итератор.
fn main() {
let nums = vec![1, 2, 3];
let _doubled = nums.iter().map(|x| {
println!("mapping {}", x);
x * 2
});
println!("nothing printed above this line");
}Потребление запускает работу
Чтобы ленивый итератор действительно начал работать, его нужно потребить. Цикл for — самый простой потребитель: он многократно вызывает next, пока не получит None.
Теперь то же замыкание map запускается для каждого элемента.
fn main() {
let nums = vec![1, 2, 3];
for x in nums.iter().map(|x| x * 2) {
println!("value {}", x);
}
}Три способа выполнить итерацию
Коллекции предоставляют три метода, создающих итераторы:
iter()— выдаёт&T(заимствует)iter_mut()— выдаёт&mut T(заимствует изменяемо)into_iter()— выдаётT(забирает владение)
fn main() {
let mut v = vec![1, 2, 3];
for x in v.iter_mut() {
*x += 10;
}
println!("{:?}", v);
}Циклы for используют into_iter
Когда Вы напрямую пишете for x in collection, Rust вызывает into_iter(), который потребляет коллекцию.
После этого цикла нельзя снова использовать v, потому что оно было перемещено в итератор.
fn main() {
let v = vec![String::from("a"), String::from("b")];
for s in v {
println!("{}", s);
}
// v is no longer usable here
}Диапазоны — это итераторы
Диапазон, например 0..5, сам является итератором и выдаёт 0, 1, 2, 3, 4. Конечное значение не включается. Для включительного диапазона используйте 0..=5.
Диапазоны отлично подходят для циклов со счётчиком без ручного отслеживания индекса.
fn main() {
for i in 0..5 {
print!("{} ", i);
}
println!();
for i in 1..=3 {
print!("{} ", i);
}
println!();
}count: простой потребитель
count потребляет весь итератор и возвращает количество выданных им элементов. Это один из множества потребляющих адаптеров.
Поскольку он потребляет итератор, повторно использовать его нельзя.
fn main() {
let total = (1..=100).count();
println!("there are {} numbers", total);
}sum: свёртка в одно значение
sum складывает все элементы и возвращает одно значение. Обычно Вы указываете целевой тип, чтобы Rust знал, результат какого типа нужно получить.
Это короче и понятнее, чем цикл с ручным накоплением.
fn main() {
let total: i32 = (1..=5).sum();
println!("sum is {}", total);
}Зачем нужны итераторы
Итераторы обеспечивают компонуемость: Вы можете объединять небольшие операции в понятные конвейеры. Кроме того, они не требуют дополнительных затрат — компилятор оптимизирует их так, чтобы они работали не медленнее написанных вручную циклов.
Освоение next, ленивости и потребления — основа всего дальнейшего материала.
Быстрая проверка
Проверьте, насколько хорошо Вы поняли трейт Iterator.
Повторение
Вы изучили основы итераторов Rust:
- Трейту
Iteratorнужны толькоnextи типItem nextвозвращаетSome(value), а затемNone- Итераторы ленивы — они ничего не делают, пока их не потребят
iter,iter_mutиinto_iterуправляют заимствованием и владением- Потребители, такие как
for,countиsum, запускают обработку
Часто задаваемые вопросы
Урок «Трейт Iterator» бесплатный?
Да — полный текст урока «Трейт Iterator» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Learn Rust Coding, подпишись на CoddyKit PRO. Курс Learn Rust Coding содержит 4 уроков всего.
Чему я научусь в уроке «Трейт Iterator»?
next и ленивые вычисления Ты практикуешь Learn Rust Coding с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Learn Rust Coding?
Предыдущий опыт не требуется. Learn Rust Coding на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Трейт Iterator»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Learn Rust Coding?
Да. Каждый урок Learn Rust Coding включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.