Сопоставить руки, возвращающие итераторы?

У меня есть код, который пытается выполнить сопоставление, где каждая ветвь может возвращать другой тип, но все эти типы реализуют Iterator<Item=usize>.

let found: Iterator<Item = usize> = match requirements {
    Requirements::A => MatchingAs { ainternals: [] },
    Requirements::B => MatchingBs { binternals: [] },
    Requirements::C => MatchingCs { cinternals: [] },
};

return found.any(|m| m == 1)

... где MatchingAs, MatchingBs и MatchingCs все impl std::iter::Iterator<Item = usize>.

Я упираюсь в стену из-за того, что Iterator не имеет размера:

    | the trait `std::marker::Sized` is not implemented for `std::iter::Iterator<Item=usize>`

Есть ли хороший подход к тому, чтобы сопоставимые руки возвращали объекты с общей характеристикой, а затем полагались (только) на эту характеристику при обработке результатов?


person Bosh    schedule 17.01.2017    source источник
comment
Пожалуйста, объясните, почему это не дубликат Правильный способ возврата итератора?   -  person Shepmaster    schedule 17.01.2017


Ответы (1)


Первый рефлекс, когда вы хотите вернуть что-то, что не Sized, - это Box это (то есть положить это в кучу, вернуть указатель):

let found: Box<Iterator<Item = usize>> = match requirements {
    Requirements::A => Box::new(MatchingAs { ainternals: [] }),
    Requirements::B => Box::new(MatchingBs { binternals: [] }),
    Requirements::C => Box::new(MatchingCs { cinternals: [] }),
};

found.any(|m| m == 1)

Здесь этого недостаточно, потому что теперь match будет жаловаться, что вы возвращаете разные типы: Box<MatchingAs>, Box<MatchingBs>, ...

Однако Box<Concrete> можно преобразовать в Box<Trait> всякий раз, когда есть impl Trait for Concrete, поэтому:

let found = match requirements {
    Requirements::A => Box::new(MatchingAs { ainternals: [] }) as Box<Iterator<Item = usize>>,
    Requirements::B => Box::new(MatchingBs { binternals: [] }) as Box<Iterator<Item = usize>>,
    Requirements::C => Box::new(MatchingCs { cinternals: [] }) as Box<Iterator<Item = usize>>,
};

found.any(|m| m == 1)

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

fn search<T: Iterator<Item = usize>>(t: T) -> bool {
    t.any(|m| m == 1)
}

а затем примените эту функцию к каждой ветви match:

match requirements {
    Requirements::A => search(MatchingAs {ainternals: []}),
    Requirements::B => search(MatchingBs {binternals: []}),
    Requirements::C => search(MatchingCs {cinternals: []}),
}

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

person Matthieu M.    schedule 17.01.2017