I'm curious for an explanation about why the below trait implementations are conflicting.
In particular, String doesn't implement IntoIterator, so the first From shouldn't apply. This can be seen by commenting out the From<String> impl -- then the program does not compile because the A::from line in main has no resolution.
Thanks!
use std::slice;
struct A<'a, I>
where I: Iterator<Item = &'a u8>
{
i: I
}
impl<'a, I, II> From<II> for A<'a, I>
where I: Iterator<Item = &'a u8>,
II: IntoIterator<IntoIter=I, Item=&'a u8>
{
fn from(ii: II) -> Self {
A { i: ii.into_iter() }
}
}
impl<'a> From<String> for A<'a, slice::Iter<'a, u8>>
{
fn from(s: String) -> Self {
A { i: s.as_bytes().iter() }
}
}
fn main() {
let i = A::from(String::from("hi"));
}
Compiling playground v0.0.1 (/playground)
error[E0119]: conflicting implementations of trait `std::convert::From<std::string::String>` for type `A<'_, std::slice::Iter<'_, u8>>`:
--> src/main.rs:18:1
|
9 | / impl<'a, I, II> From<II> for A<'a, I>
10 | | where I: Iterator<Item = &'a u8>,
11 | | II: IntoIterator<IntoIter=I, Item=&'a u8>
12 | | {
... |
15 | | }
16 | | }
| |_- first implementation here
17 |
18 | impl<'a> From<String> for A<'a, slice::Iter<'a, u8>>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `A<'_, std::slice::Iter<'_, u8>>`
|
= note: upstream crates may add a new impl of trait `std::iter::Iterator` for type `std::string::String` in future versions
error: aborting due to previous error
For more information about this error, try `rustc --explain E0119`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.
Huh, thanks for the explanation. I guess the compiler tells me that reasonably explicitly, but, in my defense, I've never heard of a compiler giving that kind of an error (i.e. "this would work, but because of something we might implement eventually it might not so we're not going to allow it").
Well, standard library is not special here - it's just another crate you depend on. So it's not "we might implement eventually", it's "implementing local trait for local type should not be breaking change".
I think this means that there is no way for me to provide a A::from(s) for a String s if I also want to support A::from(i) where i is an arbitrary iterator. In fact, I can't do A::from for any other types except types defined locally if I keep the arbitrary iterator implementation.
I've been going down the FromIterator rabbit hole, and suspect I made a wrong turn.
I'm trying take make A::from_iter(e), where e produces &u8.
I started with this:
impl<'a, I> FromIterator<&'a u8> for A<'a, I>
where
I: Iterator<Item = &'a u8>
{
fn from_iter<II: IntoIterator<Item=&'a u8>>(i: II) -> Self {
A { i: i.into_iter() }
}
}
which gives:
error[E0308]: mismatched types
--> src/main.rs:16:16
|
16 | A { i: i.into_iter() }
| ^^^^^^^^^^^^^ expected type parameter `I`, found associated type
|
= note: expected type parameter `I`
found associated type `<II as std::iter::IntoIterator>::IntoIter`
= note: you might be missing a type parameter or trait bound
Ok. It needs to be told to unify the IntoIter and the I. I flailed through various approaches to this with no success. If I move the II to the impl, I get that it is "not constrained by the impl trait, self type, or predicates" (even when it apparently is constrained in the where clauses).
Is there a better way of approaching this? This project is trying to live within an "everything is an Iterator" paradigm, which Rust sortof feels like it wants to support -- except I am pretty quickly running into issues like the above. Maybe I need to give up on that kind of approach all together?
Hmm, right, you can't directly store the iterator there because it's a generic argument of the method, not applicable to Self. At most you could store it as a trait object, Box<dyn Iterator>.
FromIterator is really meant for consuming the iterator for its items, like its primary use in collect, so maybe that was a bad suggestion for your case.
Do you need it to be a trait? You could make an inherent A::from_iter constructor instead.
I'll do that, but I'll probably stay away from the A::from_iter -- I don't want to accidentally give the impression that I've implemented FromIterator...