简体   繁体   中英

Implementing a trait for multiple types at once

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.

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.

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() {}

Using the duplicate attribute macro you can do the following:

use duplicate::duplicate;
#[duplicate(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 to avoid repeating your struct definitions:

use duplicate::duplicate;
#[duplicate(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(
     mod_name struct_name; 
     [a]      [A]; 
     [b]      [B];
)]
mod mod_name {
    pub struct name {
        x: u32,
    }
    impl T for name {
        fn double(&self) -> u32 {
            self.x * 2
        }
    }
}
mod a;
mod b;
pub use self::{a::*, b::*};

Since the internals of your structs are the same / share common components, you should extract them into a common struct and embed the common part back into the parent structs. The common struct would have the "complicated" implementation of the trait and then the parent struct's trait implementations would delegate to the common implementation:

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

struct A {
    common: Common,
}

impl T for A {
    fn double(&self) -> u32 {
        self.common.double()
    }
}

struct B {
    common: Common,
}

impl T for B {
    fn double(&self) -> u32 {
        self.common.double()
    }
}

struct Common {
    x: u32,
}

impl T for Common {
    fn double(&self) -> u32 {
        self.x * 2
    }
}

Any nicer code will require changes to the language. Two possible paths:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM