Как определить итоговый тип итератора Rust? - PullRequest
0 голосов
/ 07 июня 2019

Я выполняю шорох упражнений, чтобы выучить Rust. Я выполнил упражнение iterator3.rs и застрял. В этом упражнении меня попросили предоставить строку кода, которая будет отображать результаты одного типа в другой как часть операции; Мне нужно заполнить строку x = правильной операцией. Есть две части - первая читается частично:

let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
let x = ???
assert_eq!(format!("{:?}", x), "Ok([1, 11, 1426, 3])");

Следующее то же самое с немного другим форматом для подтверждения вывода:

assert_eq!(format!("{:?}", x), "[Ok(1), Ok(11), Ok(1426), Ok(3)]");

Мне кажется, я понимаю, что первый экземпляр должен возвращать Result, который содержит Vector of i32 или какой-либо тип ошибки. Второй должен возвращать вектор результатов, каждый из которых имеет тип i32 или тип ошибки.

Однако мне, как правило, трудно понять, как определить, какой тип возвращается комбинациями into_iter, map и collect. Я мог бы использовать некоторую помощь в обучении рассуждению об этом или в получении помощи компилятора.

Вот где я сейчас нахожусь:

Я не понимаю, что такое результирующий тип Division_results. Я пытался использовать сообщения об ошибках компилятора, а также ответ на этот вопрос , чтобы выяснить это, но результаты для меня непрозрачны, возможно, из-за ленивой оценки. Например, просто заменив x на Division_results, чтобы assert отобразил типы, например:

assert_eq!(format!("{:?}", division_results), "Ok([1, 11, 1426, 3])");

Дает мне этот результат:

left: `"Map { iter: IntoIter([27, 297, 38502, 81]) }"`,
right: `"Ok([1, 11, 1426, 3])"`', exercises/standard_library_types/iterator3.rs:75:9

И не ясно, каковы результаты левой стороны, так как итерации и карты не произошло. Другие разные вещи, которые я пробовал, дают похожие результаты.

Думая, что проблема в ленивой оценке, я также попытался использовать метод сбора, чтобы проверить, не вызовет ли это оценку. Например, вызов функции collect в конце строки Division_results выглядит следующим образом:

division_results = numbers.into_iter().map(|n| divide(n, 27)).collect();

Предоставляет ошибку:

cannot infer type consider giving `division_results` a type

Когда я изменяю коллекцию, чтобы сказать:

let division_results = numbers.into_iter().map(|n| divide(n, 27)).collect::<i32>();

Я получаю сообщение об ошибке, которое, как мне показалось, подсказало правильный тип:

let division_results = numbers.into_iter().map(|n| divide(n, 27)).collect::<i32>();
   |                                                                           ^^^^^^^ a collection of type `i32` cannot be built from `std::iter::Iterator<Item=std::result::Result<i32, DivisionError>>`

Итак, я попытался с типом, показанным в сообщении об ошибке:

let division_results = numbers.into_iter().map(|n| divide(n, 27)).collect::<Result<i32, DivisionError>>();

Только чтобы получить эту ошибку:

let division_results = numbers.into_iter().map(|n| divide(n, 27)).collect::<Result<i32, DivisionError>>();
   |                                                                           ^^^^^^^ a collection of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`

Я явно что-то упускаю. Может быть, вы можете сказать мне, что?

1 Ответ

1 голос
/ 07 июня 2019

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

Если вы хотите, чтобы Map действительно что-то делал, вам нужно потребляют итератор.Наиболее распространенные способы сделать это - циклы for и метод collect() (но есть много других методов, которые используют итератор, например sum(), count(), fold(), max(),…).В этом конкретном случае наиболее целесообразным является вызов метода collect(), поскольку вы хотите собрать результаты в векторе.

Вы уже выяснили, что желаемый тип для x - это Result упаковкавектор i32 или ошибка, или Result<Vec<i32>, DivisionError> в синтаксисе Rust.Поскольку collect() может выдавать много разных типов возвращаемых данных, нам нужно указать компилятору, какой из них мы хотим.Один из способов сделать это - явно указать тип x:

let x: Result<Vec<i32>, DivisionError> = division_results.collect();

. При этом используется реализация черты FromIterator, которая позволяет собирать итерируемые значения Result s вResult упаковка набора значений .

Другой упомянутый вами случай очень похож.На этот раз целевой тип представляет собой вектор из Result экземпляров, поэтому все, что вам нужно сделать, это указать другой тип.Это автоматически выберет правильную реализацию FromIterator для вас:

let x: Vec<Result<i32, DivisionError>> = division_results.collect();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...