Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:9:14
|
3 | impl<I: Iterator<Item = String>> Calories<I> {
| - this type parameter
...
9 | Self(source.into_iter())
| ---- ^^^^^^^^^^^^^^^^^^ expected type parameter `I`, found associated type
| |
| arguments to this function are incorrect
|
= note: expected type parameter `I`
found associated type `<S as IntoIterator>::IntoIter`
= note: you might be missing a type parameter or trait bound
note: tuple struct defined here
--> src/main.rs:1:8
|
1 | struct Calories<I>(I);
| ^^^^^^^^
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error
I found I enter to same error while attempting to implement FromIterator<String> for Calories<I>.
Compiler is not happy about S: IntoIterator<IntoIter = I>. playground
FromIterator<Item> is designed for things which consume the iterator altogether, and thus don't care what the IntoIter type is. They have to have one implementation for all T: IntoIterator<Item = Item> -- that's why the bound is on the trait method. It looks like you tried to work around this by changing the bounds on the trait method, but that's not allowed -- the point of traits is that implementors fulfill the prescribed ability.
Or to put it more concretely, there's no way for this to work with generics because if it did, someone could call
type VecInIt = std::vec::IntoIter<String>;
type VecDeqInIt = std::collections::vec_deque::IntoIter<String>;
/// ...
let cal: Calories<VecInIt> =
<Calories<VecInIt> as FromIterator<String>>
::from_iter::<VecDeqInIt>(iter);
But this makes no sense; you can't get a VecInIt out of a VecDeqInIt.
There is no way to "smuggle" the IntoIterator::IntoIter that's generic at the function level into the returned Self type, which is always the same type within the impl block (even if generic at the impl header level). Type erasure (Box<dyn Iterator<Item=String>>) doesn't help as the trait method has to accept even lifetime-carrying IntoIterator types, and there's no sound way to erase the lifetimes of those so that you can get the single Self type.
In summary, this trait doesn't do the task you're trying to accomplish, there's no sound way to force it to, and you need a different tool.
P.S.: Not sure if such blanket implementations are a good idea. I think I sometimes got into trouble with potentially overlapping implementations; i.e. you can't do this for several traits but only for one (Playground).
Thanks! Generic trait method indicate that one implementor type has to deal with all kind of concrete method is a very good point. Simply making the implementor generic won't fullfill such contract.
.collect is a method to terminate a chain of iterator method calls, and create a collection (or single value) from the result of executing / iterating through the iterator. If your Calories<T> type is a new iterator itself, then working with .collect is the wrong approach. If you want a nice-looking method in an iterator chain, then define your own extension trait for Iterators, similar to how the itertools crate does it.
The IteratorExt is a good pattern for implementing chain method. I also found that I can chain the constructor of Calories<T> by implemting an Apply trait. Thanks for your help again! Rust Playground
Interesting! A difference is that tap::Pipe is implemented for all T: ?Sized which makes sense because some of the additional methods (e.g. tap::Pipe::pipe_ref) work on references, thus it's possible to support types which are !Sized (like [u8]).