How do I implement a trait I don't own for a type I don't own?

ghz 21days ago ⋅ 42 views

I wanted to implement the Shl trait for Vec, the code is below. This would make things like vec << 4 possible, which would be nice sugar for vec.push(4).

use std::ops::Shl;

impl<T> Shl<T> for Vec<T> {
    type Output = Vec<T>;

    fn shl(&self, elem: &T) -> Vec<T> {
        self.push(*elem);
        *self
    }
}

fn main() {
    let v = vec![1, 2, 3];
    v << 4;
}

The compilation fails with the following error:

cannot provide an extension implementation where both trait and type are not defined in this crate [E0117]

or

type parameter T must be used as the type parameter for some local type (e.g. MyStruct<T>); only traits defined in the current crate can be implemented for a type parameter [E0210]

As I understand it, I'd have to patch the stdlib, more specifically the collections::vec crate. Is there another way to change this code to compile successfully?

Answer

You are correct that you cannot directly implement the Shl trait for Vec<T> because Vec<T> is part of the standard library, and Rust's orphan rule prevents you from implementing traits for types that are not locally defined (like Vec<T>).

However, there are several approaches you can use to achieve similar functionality without violating the orphan rule. I'll go over a few possible solutions:

1. Implement a Wrapper Type for Vec<T>

One way to achieve this is to create your own wrapper type around Vec<T> and then implement the Shl trait for that wrapper. This allows you to define custom behavior for the << operator without modifying Vec directly.

Example:

use std::ops::Shl;

struct MyVec<T> {
    vec: Vec<T>,
}

impl<T> MyVec<T> {
    fn new() -> Self {
        MyVec { vec: Vec::new() }
    }

    fn push(&mut self, elem: T) {
        self.vec.push(elem);
    }
}

impl<T> Shl<T> for MyVec<T> {
    type Output = MyVec<T>;

    fn shl(mut self, elem: T) -> MyVec<T> {
        self.push(elem);
        self
    }
}

fn main() {
    let mut v = MyVec::new();
    v = v << 4; // Now this works as expected
    println!("{:?}", v.vec); // [4]
}

In this example:

  • MyVec<T> wraps a Vec<T>.
  • The Shl trait is implemented for MyVec<T>, so you can use the << operator to push elements onto the vector.
  • You can create your own methods (like push) to interact with the Vec inside MyVec.

2. Implement a PushShl Trait for Vec<T>

Another option is to implement a custom trait for Vec<T> (or any other container) that works with the << operator. This way, you can keep using the << operator for pushing values without directly altering Vec.

Example:

use std::ops::Shl;

trait PushShl<T> {
    fn push_shl(&mut self, elem: T);
}

impl<T> PushShl<T> for Vec<T> {
    fn push_shl(&mut self, elem: T) {
        self.push(elem);
    }
}

fn main() {
    let mut v = vec![1, 2, 3];
    v.push_shl(4); // Using the push_shl method to mimic <<
    println!("{:?}", v); // [1, 2, 3, 4]
}

This method doesn't require overloading <<, but still allows you to add functionality to Vec<T> without violating the orphan rule.

3. Use a Custom Macro

If you really want to use the << syntax specifically, you can create a custom macro to mimic the behavior you're after.

Example:

macro_rules! vec_push {
    ($vec:expr, $elem:expr) => {
        $vec.push($elem);
    };
}

fn main() {
    let mut v = vec![1, 2, 3];
    vec_push!(v, 4); // Using the custom macro
    println!("{:?}", v); // [1, 2, 3, 4]
}

While this doesn't allow you to use << directly, the macro allows you to make the syntax feel more concise and expressive.

Conclusion

You cannot implement the Shl trait directly for Vec<T> because of the orphan rule, but you can achieve similar behavior by:

  • Wrapping Vec<T> in your own type (like MyVec).
  • Implementing custom traits (like PushShl).
  • Using a macro to simulate the behavior of the << operator.

If you just want concise code for pushing elements onto a vector, I'd recommend going with a custom macro or wrapper type, depending on your use case.