Допустим, у меня есть:
let it = [1, 2, 3].into_iter();
let jt = [4, 5, 6].into_iter();
let kt = [7, 8, 9].into_iter();
Тогда у меня есть логические условия i
, j
и k
. Я хочу создать итератор, который условно связывает it
, jt
и kt
вместе на основе значений i
, j
и k
. Могу ли я сделать это, используя только встроенную функциональность Rust Iterator
?
Вы столкнетесь с небольшой проблемой, если захотите использовать голые итераторы:
Если написать следующее:
let iter = [1, 2, 3].into_iter();
let iter = if some_condition {
iter.chain([4, 5, 6])
} else {
iter
}
Вы получите ошибку, которая сводится к следующему:
= note: expected struct `std::iter::Chain<std::array::IntoIter<_, _>, std::array::IntoIter<{integer}, 3>>`
found struct `std::array::IntoIter<_, _>`
iter
имеет тип IntoIter
, но iter.chain()
имеет тип Chain<IntoIter, ...>
Чтобы обойти это, у вас есть несколько вариантов:
interface
из таких языков, как Java, но немного теряет производительность:let iter = [1, 2, 3].into_iter();
let mut iter: Box<dyn Iterator<Item = i32>> = Box::new(iter);
if some_condition {
iter = Box::new(iter.chain([4, 5, 6]));
}
Vec
:// save heap allocations by pre-allocating the whole vec
let len = if some_condition { 6 } else { 3 };
let mut items = Vec::with_capacity(len);
items.extend([1, 2, 3]);
if some_condition {
items.extend([4, 5, 6]);
}
Вы можете превратить Option
в итератор.
let it = i.then_some([1, 2, 3]).into_iter().flatten();
let jt = j.then_some([4, 5, 6]).into_iter().flatten();
let kt = k.then_some([7, 8, 9]).into_iter().flatten();
let iter = it.chain(jt).chain(kt);
Если условие ложно, то condition.then_some(...)
вернет None
, создав пустой итератор. В противном случае возвращается Some(...)
. into_iter().flatten()
превратит Option<impl IntoIterator<Item=T>>
в impl Iterator<Item=T>
.
Это хорошее применение ящику либо. Either
реализует Iterator
, когда и левая, и правая стороны также реализуют Iterator
, поэтому его можно легко использовать для объединения итераторов в цепочку.
Имея любые три итератора it
, jt
и kt
, которые выполняют итерацию по одному и тому же Item
, с сопутствующими логическими значениями i
, j
и k
, вы можете написать функцию, которая связывает их вместе следующим образом:
use either::Either;
use std::iter;
fn chain<'a, I, J, K, Item>(
it: I,
jt: J,
kt: K,
i: bool,
j: bool,
k: bool,
) -> iter::Chain<
iter::Chain<Either<I, iter::Empty<Item>>, Either<J, iter::Empty<Item>>>,
Either<K, iter::Empty<Item>>,
>
where
I: Iterator<Item = Item>,
J: Iterator<Item = Item>,
K: Iterator<Item = Item>,
{
let iter = if i {
Either::Left(it)
} else {
Either::Right(iter::empty())
};
let iter = iter.chain(if j {
Either::Left(jt)
} else {
Either::Right(iter::empty())
});
let iter = iter.chain(if k {
Either::Left(kt)
} else {
Either::Right(iter::empty())
});
iter
}
Вызов этой функции приведет к условному вводу итератора. Например, вызов
let it = [1, 2, 3].into_iter();
let jt = [4, 5, 6].into_iter();
let kt = [7, 8, 9].into_iter();
chain(it, jt, kt, true, false, true).collect::<Vec<_>>();
дает
[1, 2, 3, 7, 8, 9]
как и ожидалось.
Вы можете попробовать это, используя эту игровую площадку.