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 aVec<T>
.- The
Shl
trait is implemented forMyVec<T>
, so you can use the<<
operator to push elements onto the vector. - You can create your own methods (like
push
) to interact with theVec
insideMyVec
.
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 (likeMyVec
). - 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.