Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a trait for multiple types at once

Tags:

rust

I have two structs and a trait:

struct A {
    x: u32,
}

struct B {
    x: u32,
}

trait T {
    fn double(&self) -> u32;
}

I would like to implement T for both structs using x.

Is there a way to write something like

impl T for A, B {
    fn double(&self) -> u32 {
        /* ... */
    }
}

I would like to not use macros if possible.

like image 957
jz87 Avatar asked Aug 25 '16 16:08

jz87


3 Answers

The only way to implement a trait once for many concrete types is to implement a trait for all types already implementing another trait.

For example, you can implement a marker trait Xed and then:

impl<T> Double for T
where
    T: Xed,
{
    fn double(&self) {
        /* ... */
    }
}

However, Rust has principled generics. The only thing that you know about T in the previous implementation is that T implements the Xed trait, and therefore the only associated types/functions you can use are those coming from Xed.

A trait cannot expose a field/attribute, only associated types, constants and functions, so Xed would need a getter for x (which need not be called x).

If you wish to rely on syntactic (and not semantic) properties of the code, then use macros.

like image 139
Matthieu M. Avatar answered Nov 17 '22 17:11

Matthieu M.


Creating a macro also solves your problem:

struct A {
    x: u32,
}

struct B {
    x: u32,
}

trait T {
    fn double(&self) -> u32;
}

macro_rules! impl_T {
    (for $($t:ty),+) => {
        $(impl T for $t {
            fn double(&self) -> u32 {
                self.x * 2
            }
        })*
    }
}

impl_T!(for A, B);

fn main() {}
like image 23
Shepmaster Avatar answered Nov 17 '22 16:11

Shepmaster


Using the duplicate_item attribute macro you can do the following:

use duplicate::duplicate_item;
#[duplicate_item(name; [A]; [B])]
impl T for name {
    fn double(&self) -> u32 {
        self.x * 2
    }
}

This will expand to two identical implementations for the two structs. I know you said you didn't want to use macros, but I interpret that as meaning you don't want to roll your own macro, so I think this is a good compromise.

You could also use duplicate_item to avoid repeating your struct definitions:

use duplicate::duplicate_item;
#[duplicate_item(name; [A]; [B])]
struct name {
    x: u32,
}

Or go all-out if you for some reason need two identical structs with identical implements (at this point we should begin questioning why we need 2 structs at all :D):

use duplicate::duplicate;
duplicate!{
    [ name; [A]; [B] ]
    pub struct name {
        x: u32,
    }
    impl T for name {
        fn double(&self) -> u32 {
            self.x * 2
        }
    }
}

Notice the use of the duplicate function-like macro this time to duplicate struct and implement at the same time.

like image 10
Emoun Avatar answered Nov 17 '22 16:11

Emoun