I’m learning rust, and one of the most basic things I want to do is to take one vector of homogenous type A which is convertible to another type B (since From<> is implemented and thus we can use .into()). When I tried running the below I got the following:
struct A {
x: String
}
struct B {
x: String
}
impl From<A> for B {
fn from(a: A) -> Self {
B { x: a.x }
}
}
impl B {
pub fn from_many<T: Into<B> + Clone>(v: Vec<T>) -> Self {
B { x: v.iter().map(|e| B::from(e.clone()).x).collect::<Vec<String>>().join(" ") }
}
}
fn main() {
...
}
I got:
error[E0277]: the trait bound `B: From<T>` is not satisfied
--> src/main.rs:17:41
|
17 | B { x: v.iter().map(|e| B::from(e.clone()).x).collect::<Vec<String>>().join(" ") }
| ------- ^^^^^^^^^ the trait `From<T>` is not implemented for `B`
| |
| required by a bound introduced by this call
|
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
15 | impl B where B: From<T> {
| ++++++++++++++++
I originally tried it without the clone(), but thought that it didn’t accept references:
...
impl B {
pub fn from_many<T: Into<B>>(v: Vec<T>) -> Self {
B { x: v.iter().map(|e| B::from(e).x).collect::<Vec<String>>().join(" ") }
}
}
...
which yielded:
error[E0277]: the trait bound `B: From<&T>` is not satisfied
--> src/main.rs:17:41
|
17 | B { x: v.iter().map(|e| B::from(e).x).collect::<Vec<String>>().join(" ") }
| ------- ^ the trait `From<&T>` is not implemented for `B`
| |
| required by a bound introduced by this call
|
= help: the trait `From<A>` is implemented for `B`
I’m not asking for an arbitrary T here, I’m asking for T which has Into<B> for T defined (in this case, I believe it’s defined since I defined the From trait). Did I do something stupid here?
>Solution :
You’re missing a very simple but easy to miss fact: if you have a From<T> for U implementation you automatically have a Into<U> for T implementation, but the opposite is not true. Thus, if by generics you require T: Into<B> (which is the right thing to do since it is more generic than B: From<T>), you need to use .into() and not B::from():
impl B {
pub fn from_many<T: Into<B> + Clone>(v: Vec<T>) -> Self {
B { x: v.iter().map(|e| e.clone().into().x).collect::<Vec<String>>().join(" ") }
}
}
Another, but unrelated, thing you’re missing is that since you have an owned Vec<T> you can use into_iter() and then you don’t need to .clone():
impl B {
pub fn from_many<T: Into<B>>(v: Vec<T>) -> Self {
B { x: v.into_iter().map(|e| e.into().x).collect::<Vec<String>>().join(" ") }
}
}