Search code examples
rusttraits

Is possible to implement traits on foreign types?


I want to implement the frequent math multiplication of a vector by a scalar: k * v = (k * v0, k * v1, k* v2..). The code is the following:

#[derive(Debug)]
struct V(Vec<i32>);

impl std::ops::Mul<V> for i32 {
    type Output = V;
    fn mul(self, v: V) -> V {
        V(v.0.iter().map(|v| v * self).collect())
    }
}

fn main() {
    let v = V(vec![1,2]);
    println!("{:?}", 3 * v);
}

This solution works, however I would like to avoid the definition of the struct V, and instead code something like that:

impl std::ops::Mul<Vec<i32>> for i32 {
    type Output = Vec<i32>;
    fn mul(self, v: Vec<i32>) -> Vec<i32> {
        v.iter().map(|v| v * self).collect()
    }
}

Which throws the following error:

 only traits defined in the current crate can be implemented for arbitrary types
  --> src/main.rs:45:1
   |
45 | impl std::ops::Mul<Vec<i32>> for i32 {
   | ^^^^^-----------------------^^^^^---
   | |    |                           |
   | |    |                           `i32` is not defined in the current crate
   | |    `Vec` is not defined in the current crate
   | impl doesn't use only types from inside the current crate
   |
   = note: define and implement a trait or new type instead

Is possible to use the multiplication trait for foreign types?


Solution

  • In short, no.

    You're running into the "orphan rule". The basic idea is that if you want to implement a trait X, on a struct/enum/union Y, at least one of those must be defined in the current crate.

    This is somewhat restrictive at times, but it is a product of Rusts "coherence rules". In general, every combination of traits and types must have at most 1 implementation. So the following code is not valid:

    struct Wrapper<T>(T);
    
    trait Print {
      fn print(&self);
    }
    
    impl Print for Wrapper<i32> {
      fn print(&self) {
        println!("a very special int: {}", self);
      }
    }
    
    impl<T: Display> Print for Wrapper<T> {
      fn print(&self) {
        print!("a normal type: {}", self);
      }
    }
    

    Imagine you write in your code 0i32.print(). Which implementation does rustc choose? The answer is unclear (in a pre-specialization world), so given Rust's principle of being explicit, the code is outright rejected.

    The orphan rule is a mechanism that generally makes the ecosystem more usable. There's nothing inherently unsound about breaking the orphan rule (if it were possible), however, it leads to an unfortunate scenario where you might use 2 crates that define their own implementations for certain foreign types and traits.

    Imagine, for example, a crate decides to add this (fairly reasonable looking) implementation:

    impl<T> Display for Vec<T> where T: Display {
      fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        todo!()
      }
    }
    

    This is fine, until you want to import another crate also defines a similar impl. Even if the code is identical, these would be separate implementations, and therefore would be incoherent and would fail to compile.

    The orphan rule protects the ecosystem from this sort of problem.

    Wrappers are idiomatic in Rust, and don't impose a runtime cost. If you find yourself writing a lot, I have written a library to automate a lot of the boilerplate called microtype which may be useful.