Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions src/adaptors/map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use std::iter::FromIterator;
use std::marker::PhantomData;

#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct MapSpecialCase<I, F> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct MapSpecialCase<I, F> {
pub struct MapSpecialCase<I, F: ?Sized> {

This way unsized “special functions” can be used, including trait objects.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good observation! Are unsized functions common? Alternatively, we could reverse the order of fields in MapSpecialCase and make I: ?Sized, thereby supporting iterator trait objects. (Unfortunately, it's one or the other.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this nice idea. In general, I am leaning to incorporate changes that generalize functionality.

However, I'd like to postpone this one and - if we decide to accept ?Sized functions - do it uniformly throughout itertools. I think my changes did not change behavior in this regard, so I hope postponing is ok.

@pthariensflame As I'm no expert in this area: Could you showcase a situation that requires F: ?Sized?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! If we have an unsized trait object dyn Fn(X) -> Y, then we get an unsized MapSpecialCaseFnOk<dyn Fn(X) -> Y>, which is then your example of an unsized F.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, I'd like to postpone this one and - if we decide to accept ?Sized functions - do it uniformly throughout itertools. I think my changes did not change behavior in this regard, so I hope postponing is ok.

Fine by me!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pthariensflame I'm curious about the actual use-case dyn Fn might appear in. I've never needed to dynamically dispatched function objects before.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm fine with waiting too. This isn't essential functionality, just mildly helpful at best.

As far as dyn Fn, I can't point to any specific code, but it might come up when abstracting over dynamically swappable actions. For example: imagine a pipeline of scriptable blocks that the user can specify in a GUI, and it gets compiled down to functions that invoke the scripts and munge the values flowing through as necessary. The whole pipeline is then composed and boxed up as a dyn Fn so that it can be altered on the fly as the application runs.

iter: I,
f: F,
}

pub trait MapSpecialCaseFn<T> {
type Out;
fn call(&mut self, t: T) -> Self::Out;
}

impl<I, R> Iterator for MapSpecialCase<I, R>
where
I: Iterator,
R: MapSpecialCaseFn<I::Item>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
R: MapSpecialCaseFn<I::Item>,
R: ?Sized + MapSpecialCaseFn<I::Item>,

{
type Item = R::Out;

fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|i| self.f.call(i))
}

fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}

fn fold<Acc, Fold>(self, init: Acc, mut fold_f: Fold) -> Acc
where
Fold: FnMut(Acc, Self::Item) -> Acc,
{
let mut f = self.f;
self.iter.fold(init, move |acc, v| fold_f(acc, f.call(v)))
}

fn collect<C>(self) -> C
where
C: FromIterator<Self::Item>,
{
let mut f = self.f;
self.iter.map(move |v| f.call(v)).collect()
}
}

impl<I, R> DoubleEndedIterator for MapSpecialCase<I, R>
where
I: DoubleEndedIterator,
R: MapSpecialCaseFn<I::Item>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
R: MapSpecialCaseFn<I::Item>,
R: ?Sized + MapSpecialCaseFn<I::Item>,

{
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back().map(|i| self.f.call(i))
}
}

impl<I, R> ExactSizeIterator for MapSpecialCase<I, R>
where
I: ExactSizeIterator,
R: MapSpecialCaseFn<I::Item>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
R: MapSpecialCaseFn<I::Item>,
R: ?Sized + MapSpecialCaseFn<I::Item>,

{
}

/// An iterator adapter to apply a transformation within a nested `Result::Ok`.
///
/// See [`.map_ok()`](../trait.Itertools.html#method.map_ok) for more information.
pub type MapOk<I, F> = MapSpecialCase<I, MapSpecialCaseFnOk<F>>;

/// See [`MapOk`](struct.MapOk.html).
#[deprecated(note = "Use MapOk instead", since = "0.10")]
pub type MapResults<I, F> = MapOk<I, F>;

impl<F, T, U, E> MapSpecialCaseFn<Result<T, E>> for MapSpecialCaseFnOk<F>
where
F: FnMut(T) -> U,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
F: FnMut(T) -> U,
F: ?Sized + FnMut(T) -> U,

{
type Out = Result<U, E>;
fn call(&mut self, t: Result<T, E>) -> Self::Out {
t.map(|v| self.0(v))
}
}

#[derive(Clone)]
pub struct MapSpecialCaseFnOk<F>(F);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct MapSpecialCaseFnOk<F>(F);
pub struct MapSpecialCaseFnOk<F: ?Sized>(F);


/// Create a new `MapOk` iterator.
pub fn map_ok<I, F, T, U, E>(iter: I, f: F) -> MapOk<I, F>
where
I: Iterator<Item = Result<T, E>>,
F: FnMut(T) -> U,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
F: FnMut(T) -> U,
F: ?Sized + FnMut(T) -> U,

{
MapSpecialCase {
iter,
f: MapSpecialCaseFnOk(f),
}
}

/// An iterator adapter to apply `Into` conversion to each element.
///
/// See [`.map_into()`](../trait.Itertools.html#method.map_into) for more information.
pub type MapInto<I, R> = MapSpecialCase<I, MapSpecialCaseFnInto<R>>;

impl<T: Into<U>, U> MapSpecialCaseFn<T> for MapSpecialCaseFnInto<U> {
type Out = U;
fn call(&mut self, t: T) -> Self::Out {
t.into()
}
}

#[derive(Clone)]
pub struct MapSpecialCaseFnInto<U>(PhantomData<U>);

/// Create a new [`MapInto`](struct.MapInto.html) iterator.
pub fn map_into<I, R>(iter: I) -> MapInto<I, R> {
MapSpecialCase {
iter,
f: MapSpecialCaseFnInto(PhantomData),
}
}
116 changes: 4 additions & 112 deletions src/adaptors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
//! except according to those terms.

mod coalesce;
mod map;
mod multi_product;
pub use self::coalesce::*;
pub use self::map::{map_into, map_ok, MapInto, MapOk};
#[allow(deprecated)]
pub use self::map::MapResults;
#[cfg(feature = "use_std")]
pub use self::multi_product::*;

Expand Down Expand Up @@ -806,118 +810,6 @@ impl_tuple_combination!(Tuple2Combination Tuple1Combination ; A, A, A ; a);
impl_tuple_combination!(Tuple3Combination Tuple2Combination ; A, A, A, A ; a b);
impl_tuple_combination!(Tuple4Combination Tuple3Combination ; A, A, A, A, A; a b c);

/// An iterator adapter to apply `Into` conversion to each element.
///
/// See [`.map_into()`](../trait.Itertools.html#method.map_into) for more information.
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct MapInto<I, R> {
iter: I,
_res: PhantomData<R>,
}

/// Create a new [`MapInto`](struct.MapInto.html) iterator.
pub fn map_into<I, R>(iter: I) -> MapInto<I, R> {
MapInto {
iter,
_res: PhantomData,
}
}

impl<I, R> Iterator for MapInto<I, R>
where I: Iterator,
I::Item: Into<R>,
{
type Item = R;

fn next(&mut self) -> Option<Self::Item> {
self.iter
.next()
.map(|i| i.into())
}

fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}

fn fold<Acc, Fold>(self, init: Acc, mut fold_f: Fold) -> Acc
where Fold: FnMut(Acc, Self::Item) -> Acc,
{
self.iter.fold(init, move |acc, v| fold_f(acc, v.into()))
}
}

impl<I, R> DoubleEndedIterator for MapInto<I, R>
where I: DoubleEndedIterator,
I::Item: Into<R>,
{
fn next_back(&mut self) -> Option<Self::Item> {
self.iter
.next_back()
.map(|i| i.into())
}
}

impl<I, R> ExactSizeIterator for MapInto<I, R>
where
I: ExactSizeIterator,
I::Item: Into<R>,
{}

/// See [`MapOk`](struct.MapOk.html).
#[deprecated(note="Use MapOk instead", since="0.10")]
pub type MapResults<I, F> = MapOk<I, F>;

/// An iterator adapter to apply a transformation within a nested `Result::Ok`.
///
/// See [`.map_ok()`](../trait.Itertools.html#method.map_ok) for more information.
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct MapOk<I, F> {
iter: I,
f: F
}

/// Create a new `MapOk` iterator.
pub fn map_ok<I, F, T, U, E>(iter: I, f: F) -> MapOk<I, F>
where I: Iterator<Item = Result<T, E>>,
F: FnMut(T) -> U,
{
MapOk {
iter,
f,
}
}

impl<I, F, T, U, E> Iterator for MapOk<I, F>
where I: Iterator<Item = Result<T, E>>,
F: FnMut(T) -> U,
{
type Item = Result<U, E>;

fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|v| v.map(&mut self.f))
}

fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}

fn fold<Acc, Fold>(self, init: Acc, mut fold_f: Fold) -> Acc
where Fold: FnMut(Acc, Self::Item) -> Acc,
{
let mut f = self.f;
self.iter.fold(init, move |acc, v| fold_f(acc, v.map(&mut f)))
}

fn collect<C>(self) -> C
where C: FromIterator<Self::Item>
{
let mut f = self.f;
self.iter.map(move |v| v.map(&mut f)).collect()
}
}

/// An iterator adapter to filter values within a nested `Result::Ok`.
///
/// See [`.filter_ok()`](../trait.Itertools.html#method.filter_ok) for more information.
Expand Down
12 changes: 12 additions & 0 deletions tests/specializations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,15 @@ quickcheck! {
test_specializations(&i1.into_iter().merge_join_by(i2.into_iter(), std::cmp::Ord::cmp));
}
}

quickcheck! {
fn map_into(v: Vec<u8>) -> () {
test_specializations(&v.into_iter().map_into::<u32>());
}
}

quickcheck! {
fn map_ok(v: Vec<Result<u8, char>>) -> () {
test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1)));
}
}