У меня есть этот код:
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
?
Итераторы в Rust ленивы. Когда вы вызываете some_iterator.lines(...).map(...)
, фактическая итерация не выполняется. Вместо этого создается цепочка или структура «кукла», где каждый итератор оборачивает предыдущий.
Все они начнут выполняться, когда вы попытаетесь фактически использовать результат итератора. Например, читая его в цикле. Если вы хотите выполнить расчет сейчас и использовать результат позже, добавьте .collect()
в конец цепочки итераторов (возможно, вам придется указать коллекцию, которую вы хотите вернуть, .collect::<Vec<_>>()
или около того).
@Mandroid Да, вполне.
@Mandroid Правда, за исключением того, что потоки Java (и функции, которые они вызывают) возвращают интерфейс, что означает, что они (выделяются в куче и) динамически отправляются, тогда как в Rust нет ни распределения, ни динамической диспетчеризации, что означает, что цепочки итераторов компилируются чрезвычайно эффективно. машинный код, часто эквивалентный, а иногда и лучший, чем рукописные циклы.
@ChayimFriedman, если нет выделения памяти (стек или куча), то как все хранится? Их нужно как-то сохранить в памяти, не так ли? Можете ли вы уточнить?
@Mandroid: их память находится в стеке (поэтому нет выделения кучи), и они статически типизированы (поэтому нет динамической диспетчеризации).
@Mandroid В дополнение к тому, что сказал Эггьял, часто им не нужно переносить состояние (например, map()
с обратным вызовом без захвата), поэтому у них вообще нет выделения.
Также обратите внимание, что вам почти наверняка никогда не понадобится называть тип итератора, потому что вы можете либо собрать его немедленно, либо, возвращаясь из функции, можете вернуть impl Iterator<Item=Whatever>
(что скрывает от вызывающих сторон все, что касается итератора, кроме его Item
, без изменения строго типизированного типа). , кукольная природа).
Хорошо понял. Это похоже на поток Java, который также оценивается лениво, а тип возвращаемого значения для методов потока - это другой поток.