I have a struct (call it Collection) which implements IntoIterator. I'd like to be able to instantiate this struct using from, passing in another IntoIterator:
let collection = Collection::from([1, 2, 3]);
So, I believe I need to implement From<impl IntoIterator> for Collection. However, this results in conflicting implementations of From with the default blanket implementation From<T> for T.
Collection implements From<I> where I: IntoIterator
Collection implements IntoIterator
Collection could be I
This gives From<Collection> for Collection
Which has the same signature as the blanket From<T> for T, resulting in the conflict
I haven't been able to find any way around this... I was thinking I could exclude Collection from I, but this requires negative_impls, which is unstable.
This feels like a fairly common pattern? Looking at the docs for Vec<> I can see it implements From<> with brute force for the common iterable types, but I was hoping to be able to write something more generic.
I have of course already implemented FromIterator for Collection, so I can construct it with from_iter() – I just want to make the shorter from() work too.
It's never possible to implement From to convert from or to all types satisfying a property. This is part of why conversion traits other than From exist at all — for example, IntoIterator is a conversion that is blanket implemented for all Iterators.
Hold on, that can't always be true, since this is possible:
impl<I,T> From<I> for Collection<T>
where I: IntoIterator<Item = (T, T)>
{
// --snip--
}
In this case the above conflict doesn't apply because Collection<T> implements IntoIterator<Item = T>, not IntoIterator<Item = (T, T)>. Is that clash just an annoying edge case then?
Sorry, yes, you’re right; I forgot that the rules have more nuance than I said. It’s better to say: you can't write anything that conflicts with impl<T> From<T> for T.
It would be nice if there was a way to carve out that overlap by writing something like
impl<I,T> From<I> for Collection<T>
where
I: IntoIterator<Item = T>,
I != Collection<T>,
but that is in fact a very hard type-system problem in disguise as a simple one.
Coherence errors can be confusing in cases where there is no actual overlap, e.g.
due to future foreign implementations allowed under the orphan rules, which are intentionally considered overlapping even if they don't currently exist
due to limitations or flaws in the current coherence checking algorithms
But in the OP case there is actual overlap, which is at least easy to explain; I'm not sure I'd call it an edge case.[1]
...but that doesn't make it any less annoying I'm sure.
Incidentally negative_impls doesn't help since you want the IntoIterator implementation; in fact delete that implementation and the From implementation works today. You'd need something like From being specializable. ↩︎
The closest thing you can get to a negative impl is involving your own type from your own crate that will be known not to implement a trait.
struct Wrapper<I>(I);
impl<I> From<Wrapper<I>> for Collection
Unfortunately, it's not transparent and needs to be used explicitly:
Collection::from(Wrapper(i))
It can be made nicer if you're doing this for your own trait, because then you can define trait MyFrom<T, WhoImplementsIt = MyMarkerType> and have separate impls for MyFrom<T, MyMarkerType> and MyFrom<T, SomeElsesMarkerType>. But in this case From is in std and you can't change it.
You can add inherent fn from to Collection to just get the Collection::from() syntax working when used directly, but of course that won't suffice in generic contexts that want the From trait.