I'm implementing a 3D vector struct Vec3, accompanied by a NormedVec3 struct for representing normalized vectors, plus a Vector3 trait that both structs implement. I want to be able to add these vectors together in any combination or order of types and get back a Vec3, so to save on typing, I tried this:
impl<T: Vector3, V: Vector3> Add<V> for T {
type Output = Vec3;
fn add(self, rhs: V) -> Vec3 {
...
}
}
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
--> src/lib.rs:73:6
|
73 | impl<T: Vector3, V: Vector3> Add<V> for T {
| ^ type parameter `T` must be used as the type parameter for some local type
|
= note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
= note: only traits defined in the current crate can be implemented for a type parameter
But ... Tis a type parameter for some local type (Vector3), isn't it? Or is the problem that Vector3 is a trait rather than a concrete type? How can I write the necessary Add implementations with a minimum of typing?
No, T doesn't parameterize anything here. It's the type for which you are attempting to implement the trait. (Vector3 doesn't have any type parameters anyway.)
If this code were allowed, because both T and V are type variables, foreign crates would also be allowed to add an implementation of the exact same shape, causing a conflict.
OK, so, knowing that, I assume the best way to get what I want it is to write two Add implementations, one for Vector3 + Vec3 = Vec3, the other for Vector3 + NormedVec3 = Vec3. Unfortunately, trying to do the first one with impl<T: Vector3> Add<Vec3> for T { type Output = Vec3; ... } hit me with error E0210. I'm guessing that I can only implement Vector3 + Vector3 = Vec3 (where the two Vector3's are the same type) via a blanket implementation, after which I'll need to do NormedVec3 + Vec3 = Vec3 and Vec3 + NormedVec3 = Vec3 as two more trait implementations. Does that work? Is there a better/shorter way?
And now I'm finding that impl<T> Add for T where T: Vector3 doesn't work either. I thought that was how you did blanket implementations? Or would that only be an option here if Add belonged to my crate?
Yes. You have to consider that all crates are equal, so if you are allowed to write an impl of a particular shape, then everyone else is. Therefore, you always have to consider what would happen if someone else would write the same kind of impl – and if that would cause a conflict, it has to be disallowed.