Почему тип возвращаемого значения `.map(…)` для итератора настолько сложен?

У меня есть этот код:

let mut myFile = readFile();
let reader = BufReader::new(myFile);
let tasks = reader.lines()
        .map(|line| line.expect("Couldn't read line"));

Когда я навожу курсор на «задачи», я вижу это:

let tasks: Map<Lines<BufReader<File>>, fn(Result<String>) -> String>

Глядя на код:

reader.lines() дает итератор по строкам в файле. map() принимает функцию/замыкание (которая выполняет какое-то действие над каждой строкой и возвращает другое значение).

Поэтому я ожидаю, что возвращаемое значение map() будет другой структурой данных, представляющей список значений.

Почему вместо Map?

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
0
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Итераторы в Rust ленивы. Когда вы вызываете some_iterator.lines(...).map(...), фактическая итерация не выполняется. Вместо этого создается цепочка или структура «кукла», где каждый итератор оборачивает предыдущий.

Все они начнут выполняться, когда вы попытаетесь фактически использовать результат итератора. Например, читая его в цикле. Если вы хотите выполнить расчет сейчас и использовать результат позже, добавьте .collect() в конец цепочки итераторов (возможно, вам придется указать коллекцию, которую вы хотите вернуть, .collect::<Vec<_>>() или около того).

Хорошо понял. Это похоже на поток Java, который также оценивается лениво, а тип возвращаемого значения для методов потока - это другой поток.

Mandroid 07.04.2024 12:04

@Mandroid Да, вполне.

Sergio Tulentsev 07.04.2024 12:05

@Mandroid Правда, за исключением того, что потоки Java (и функции, которые они вызывают) возвращают интерфейс, что означает, что они (выделяются в куче и) динамически отправляются, тогда как в Rust нет ни распределения, ни динамической диспетчеризации, что означает, что цепочки итераторов компилируются чрезвычайно эффективно. машинный код, часто эквивалентный, а иногда и лучший, чем рукописные циклы.

Chayim Friedman 07.04.2024 14:03

@ChayimFriedman, если нет выделения памяти (стек или куча), то как все хранится? Их нужно как-то сохранить в памяти, не так ли? Можете ли вы уточнить?

Mandroid 07.04.2024 14:45

@Mandroid: их память находится в стеке (поэтому нет выделения кучи), и они статически типизированы (поэтому нет динамической диспетчеризации).

eggyal 07.04.2024 14:47

@Mandroid В дополнение к тому, что сказал Эггьял, часто им не нужно переносить состояние (например, map() с обратным вызовом без захвата), поэтому у них вообще нет выделения.

Chayim Friedman 07.04.2024 16:10

Также обратите внимание, что вам почти наверняка никогда не понадобится называть тип итератора, потому что вы можете либо собрать его немедленно, либо, возвращаясь из функции, можете вернуть impl Iterator<Item=Whatever> (что скрывает от вызывающих сторон все, что касается итератора, кроме его Item, без изменения строго типизированного типа). , кукольная природа).

BallpointBen 08.04.2024 17:10

Другие вопросы по теме

Устранение неполадок в определении концепции итератора для итераторов, подобных указателям, в C++
Как передать один раз полный набор данных одному работнику и определенные подмножества другим работникам в цикле foreach с помощью isplit()
Почему Range можно собрать в HashMap или Vec в Rust?
Как найти индекс элемента в таблице результатов, останавливаясь, если обнаружена ошибка?
В чем проблема при использовании специального распределителя в С++ при назначении контейнера итератору?
Как передать итератор в лямбда-функцию для моего целевого элемента?
Как я могу преобразовать вектор строк в Rust с помощью карты, а затем соединить строки с помощью Flat_map в одну строку?
Получение четных чисел в обратном итераторе
Итерационный посредник теперь работает последовательно с sequential="true"
Как получить среднее значение последовательности, используя итератор в Pandas?