Обработка возможных отдельных элементов внутри flat_map в Rust - PullRequest
0 голосов
/ 19 октября 2019

Можно ли обрабатывать различное количество элементов внутри flat_map в Rust?

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

Я пытался использовать std::iter::once, но он не работает, поскольку once и Vec::iter - это разные типы, которые не могут быть возвращены из одного и того же условия if.

enum Enum {
    Ident(String),
    Val(i32)
}

fn main() {
    let my_vec = vec![1, 2, 3];
    let condition = true; // Any condition

    my_vec.iter()
          .flat_map(|x| {
              if condition {
                  std::iter::once(Enum::Ident("id".to_string())).into_iter() /* single Enum element */
              } else {
                  vec![Enum::Val(1), Enum::Val(2)].iter() /* vector of Enum elements */
              }
          })
          .collect::<Vec<Enum>>()
}
   |
12 | /               if condition {
13 | |                   std::iter::once(Enum::Ident("id".to_string())).into_iter() /* single Enum element */
   | |                   ---------------------------------------------------------- expected because of this
14 | |               } else {
15 | |                   vec![Enum::Val(1), Enum::Val(2)].iter() /* vector of Enum elements */
   | |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::iter::Once`, found struct `std::slice::Iter`
16 | |               }
   | |_______________- if and else have incompatible types
   |
   = note: expected type `std::iter::Once<Enum>`
              found type `std::slice::Iter<'_, Enum>`

1 Ответ

1 голос
/ 19 октября 2019

Очевидным решением является использование вектора в обоих случаях:

my_vec
    .iter()
    .flat_map(|_| {
        if condition {
            vec![Enum::Ident("id".to_string())].into_iter()
        } else {
            vec![Enum::Val(1), Enum::Val(2)].into_iter()
        }
    })
    .collect::<Vec<Enum>>();

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

use either::{Left, Right};

enum Enum {
    Ident(String),
    Val(i32),
}

fn main() {
    let my_vec = vec![1, 2, 3];
    let _ = my_vec
        .iter()
        .flat_map(|&x| {
            if x > 1 {
                Left(std::iter::once(Enum::Ident("id".to_string())))
            } else {
                Right(vec![Enum::Val(1), Enum::Val(2)].into_iter())
            }
        })
        .collect::<Vec<Enum>>();
}

Ящик определяет общее перечисление Either с вариантами Left и Right. Если типы обеих сторон поддерживают итерацию, перечисление также поддерживает итерации путем отправки в соответствующие базовые типы итераторов.

...