Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

rust

traits

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?

like image 214
le_me Avatar asked Aug 20 '14 19:08

le_me


People also ask

How do you implement traits?

Implementing a trait on a type is similar to implementing regular methods. The difference is that after impl , we put the trait name we want to implement, then use the for keyword, and then specify the name of the type we want to implement the trait for.

How do traits work in Rust?

A trait in Rust is a group of methods that are defined for a particular type. Traits are an abstract definition of shared behavior amongst different types. So, in a way, traits are to Rust what interfaces are to Java or abstract classes are to C++. A trait method is able to access other methods within that trait.

What is blanket implementation in Rust?

What are blanket implementations? Blanket implementations leverage Rust's ability to use generic parameters. They can be used to define shared behavior using traits. This is a great way to remove redundancy in code by reducing the need to repeat the code for different types with similar functionality.

What is a trait object?

A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.


2 Answers

While you can't do that exactly, the usual workaround is to just wrap the type you want in your own type and implement the trait on that.

use somecrate::FooType; use somecrate::BarTrait;  struct MyType(FooType);  impl BarTrait for MyType {     fn bar(&self) {         // use `self.0` here     } } 
like image 137
Luqman Avatar answered Oct 06 '22 04:10

Luqman


This would make things like vec << 4 possible, which would be nice sugar for vec.push(4).

Although it can be done, it is generally a bad idea to implement an operator with a unexpected semantic.

Here is an example of how this can be done:

use std::ops::Shl;  struct BadVec<T>(Vec<T>);  impl<T> Shl<T> for BadVec<T> {     type Output = BadVec<T>;      fn shl(mut self, elem: T) -> Self::Output {         self.0.push(elem);         self     } }  fn main() {     let mut v = BadVec(vec![1, 2, 3]);     v = v << 4;     assert_eq!(vec![1, 2, 3, 4], v.0) } 

If you implement Deref (DerefMut):

use std::ops::{Deref, DerefMut};  impl<T> Deref for BadVec<T> {     type Target = Vec<T>;      fn deref(&self) -> &Self::Target {         &self.0     } }  impl<T> DerefMut for BadVec<T> {     fn deref_mut(&mut self) -> &mut Self::Target {         &mut self.0     } } 

you can call Vec methods:

fn main() {     let mut v = BadVec(vec![1, 2, 3]);     v = v << 4;     v.truncate(2);     assert_eq!(2, v.len()); } 

Take a look at the newtype_derive crate, it can generate some boilerplate code for you.

like image 20
malbarbo Avatar answered Oct 06 '22 03:10

malbarbo