Прежде всего, ваш выходной итератор, вероятно, должен быть ассоциированным типом, а не параметром признака, поскольку этот тип является выводом признака (это не то, что вызывающий может контролировать).
trait Squarable {
type Output: Iterator<Item = u8>;
fn squared(self) -> I;
}
При этом существует несколько различных подходов к решению этой проблемы, каждый из которых имеет свои преимущества и недостатки.
Использование объектов признаков
Первый заключается в использовании объектов-черт, например dyn Iterator<Item = u8>
, для удаления типа во время выполнения. Это требует небольших затрат времени выполнения, но, безусловно, является самым простым решением в стабильном Rust на сегодняшний день:
trait Squarable {
fn squared(self) -> Box<dyn Iterator<Item = u8>>;
}
impl<I: 'static + Iterator<Item = u8>> Squarable for I {
fn squared(self) -> Box<dyn Iterator<Item = u8>> {
Box::new(self.map(|x| x * x))
}
}
Использование настраиваемого типа итератора
В стабильной ржавчине это определенно самый чистый из с точки зрения пользователя черты, однако для его реализации требуется немного больше кода, потому что вам нужно написать собственный тип итератора. Однако для простого итератора map
это довольно просто:
trait Squarable: Sized {
fn squared(self) -> SquaredIter<Self>;
}
impl<I: Iterator<Item = u8>> Squarable for I {
fn squared(self) -> SquaredIter<I> {
SquaredIter(self)
}
}
struct SquaredIter<I>(I);
impl<I: Iterator<Item = u8>> Iterator for SquaredIter<I> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
self.0.next().map(|x| x * x)
}
}
Использование явного Map
type
<I as Iterator>::map(f)
имеет тип std::iter::Map<I, F>
, поэтому если Тип F
функции отображения известен, мы можем использовать этот тип явно, без затрат времени выполнения. Тем не менее, в нем указывается указанный тип c как часть типа возвращаемого значения функции, что затрудняет его замену в будущем без нарушения зависимого кода. В большинстве случаев функция также не будет известна; в этом случае мы можем использовать F = fn(u8) -> u8
, однако, так как функция не сохраняет никакого внутреннего состояния (но часто это не работает).
trait Squarable: Sized {
fn squared(self) -> std::iter::Map<Self, fn(u8) -> u8>;
}
impl<I: Iterator<Item = u8>> Squarable for I {
fn squared(self) -> std::iter::Map<Self, fn(u8) -> u8> {
self.map(|x| x * x)
}
}
Используя связанный тип
Альтернативный к вышесказанному - придать признаку ассоциированный тип. Это по-прежнему имеет ограничение на то, что тип функции должен быть известен, но он немного более общий, поскольку тип Map<...>
связан с реализацией, а не с самой чертой.
trait Squarable {
type Output: Iterator<Item = u8>;
fn squared(self) -> Self::Output;
}
impl<I: Iterator<Item = u8>> Squarable for I {
type Output = std::iter::Map<Self, fn(u8) -> u8>;
fn squared(self) -> Self::Output {
self.map(|x| x * x)
}
}
Использование impl
в связанный тип
Это похоже на приведенное выше «Использование связанного типа», но вы можете полностью скрыть фактический тип, за исключением того факта, что это итератор. Лично я считаю, что это предпочтительное решение, но, к сожалению, оно все еще нестабильно (зависит от функции type_alias_impl_trait
), поэтому вы можете использовать его только в ночной Rust.
#![feature(type_alias_impl_trait)]
trait Squarable {
type Output: Iterator<Item = u8>;
fn squared(self) -> Self::Output;
}
impl<I: Iterator<Item = u8>> Squarable for I {
type Output = impl Iterator<Item = u8>;
fn squared(self) -> Self::Output {
self.map(|x| x * x)
}
}