Условно перебрать один из нескольких возможных iteratorов

Я пытаюсь переключить поведение на основе ввода параметра в функцию. Идея состоит в том, чтобы итерации основываться на том, присутствует ли данный Option . Вот минимальный, если глупый пример:

 use std::iter; fn main() { let x: Option = None; // Repeat x 5 times if present, otherwise count from 1 to 5 for i in match x { None => 1..5, Some(x) => iter::repeat(x).take(5), } { println!("{}", i); } } 

Я получаю сообщение об ошибке:

 error[E0308]: match arms have incompatible types --> src/main.rs:7:14 | 7 | for i in match x { | ______________^ 8 | | None => 1..5, 9 | | Some(x) => iter::repeat(x).take(5), | | ----------------------- match arm with an incompatible type 10 | | } { | |_____^ expected struct `std::ops::Range`, found struct `std::iter::Take` | = note: expected type `std::ops::Range` found type `std::iter::Take<std::iter::Repeat>` 

Разумеется, это имеет смысл, но мне бы очень хотелось выбрать мой iterator на основе условия, поскольку код в for-loop нетривиальен и копирует все это просто для изменения выбора iteratorа. уродливым и неподъемным.

Я попытался использовать в as Iterator на обоих плечах, но это дает мне ошибку о нестандартных типах, потому что это объект-признак. Есть ли простой способ сделать это?

Я мог бы, конечно, использовать .collect() поскольку они возвращают один и тот же тип и перебирают этот вектор. Это хорошее быстрое исправление, но для больших списков кажется немного чрезмерным.

В любом ящике Either тип. Если обе половины из них являются iteratorами, то и тот, и Either :

 extern crate either; use either::Either; use std::iter; fn main() { let x: Option = None; // Repeat x 5 times if present, otherwise count from 1 to 5 let iter = match x { None => Either::Left(1..5), Some(x) => Either::Right(iter::repeat(x).take(5)), }; for i in iter { println!("{}", i); } } 

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

Этот тип также может быть возвращен из функции , в отличие от ссылок на объекты объектов. По сравнению с объектами с боксами он всегда будет использовать фиксированный размер в стеке, независимо от того, какой конкретный тип был выбран.

Вы найдете этот тип (или семантический эквивалент) в других местах, например, futures::Either

Вам нужно иметь ссылку на признак :

 use std::iter; fn main() { let mut a; let mut b; let x: Option = None; // Repeat x 5 times if present, otherwise count from 1 to 5 let iter: &mut Iterator = match x { None => { a = 1..5; &mut a } Some(x) => { b = iter::repeat(x).take(5); &mut b } }; for i in iter { println!("{}", i); } } 

Основной недостаток этого решения заключается в том, что вам нужно выделять пространство стека для каждого конкретного типа, который у вас есть. Это также означает переменные для каждого типа. Хорошо, что нужно использовать только использованный тип.

Та же идея, но требует выделения кучи, заключается в использовании объектов с боксами :

 use std::iter; fn main() { let x: Option = None; // Repeat x 5 times if present, otherwise count from 1 to 5 let iter: Box> = match x { None => Box::new(1..5), Some(x) => Box::new(iter::repeat(x).take(5)), }; for i in iter { println!("{}", i); } } 

Это в основном полезно, когда вы хотите вернуть iterator из функции . Пространство стека – это единственный указатель, и будет выделено только необходимое кучное пространство.

Лично, вместо того, чтобы использовать Either , я часто предпочитаю создавать серию значений Option которые соединяются вместе. Что-то вроде этого:

детская площадка

 use std::iter; fn main() { let x: Option = None; // Repeat x 5 times if present, otherwise count from 1 to 5 for i in pick(x) { println!("{}", i); } } fn pick(opt_x: Option) -> impl Iterator { let iter_a = if let None = opt_x { Some(1..5) } else { None }; let iter_b = if let Some(x) = opt_x { Some(iter::repeat(x).take(5)) } else { None }; iter_a.into_iter().flatten().chain(iter_b.into_iter().flatten()) } из них use std::iter; fn main() { let x: Option = None; // Repeat x 5 times if present, otherwise count from 1 to 5 for i in pick(x) { println!("{}", i); } } fn pick(opt_x: Option) -> impl Iterator { let iter_a = if let None = opt_x { Some(1..5) } else { None }; let iter_b = if let Some(x) = opt_x { Some(iter::repeat(x).take(5)) } else { None }; iter_a.into_iter().flatten().chain(iter_b.into_iter().flatten()) } из них use std::iter; fn main() { let x: Option = None; // Repeat x 5 times if present, otherwise count from 1 to 5 for i in pick(x) { println!("{}", i); } } fn pick(opt_x: Option) -> impl Iterator { let iter_a = if let None = opt_x { Some(1..5) } else { None }; let iter_b = if let Some(x) = opt_x { Some(iter::repeat(x).take(5)) } else { None }; iter_a.into_iter().flatten().chain(iter_b.into_iter().flatten()) } 

Это немного менее очевидно, чем использование Either , но он избегает другого ящика, и иногда он работает довольно элегантно.

  • Как преобразовать 'struct' в '& '?
  • Как проверить равенство между объектами признаков?
  • Как вызвать метод, когда свойство и структура используют одно и то же имя?
  • Печатает! заимствовать или владеть переменной?
  • Как сделать бинарный поиск на Vec плавающих?
  • Почему я не могу сохранить значение и ссылку на это значение в одной и той же структуре?
  • В чем разница между iter и in_iter?
  • Измените вариант enums при перемещении поля в новый вариант
  • Можно ли управлять размером массива с помощью параметра типа общего?
  • Возвращает локальную строку как срез (& str)
  • Как создать глобальный, изменяемый синглтон?
  • Давайте будем гением компьютера.