45

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
jz87
  • 9,199
  • 10
  • 37
  • 42

4 Answers4

31

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 1
    The thing is, I would like to access fields in my trait implementation. Hence the problem. – jz87 Aug 25 '16 at 20:00
  • @jz87: Make a getter in the `Xed` trait or use a macro, as mentioned. There is no support for "mapping fields" in traits to Rust (and there may never be, as getters are sufficient). – Matthieu M. Aug 25 '16 at 20:54
  • Would there be a way to maintain that getter private while the other trait methods are public? – eldruin Sep 24 '18 at 07:13
  • @eldruin: Trait methods are always public, but trait themselves can be private. To maintain the getter private you would implement either directly on the type (inherent method) or on a private trait. – Matthieu M. Sep 24 '18 at 10:01
  • 1
    For me this fails with `type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct`)` (even if the Trait `Xed` is local to the module). – Florian Jan 03 '23 at 22:11
  • @Florian: I... have no idea what you're talking about. If you can't find a solution to your problem, feel free to ask a question or hop in the [rust chat](https://chat.stackoverflow.com/rooms/62927/rust). – Matthieu M. Jan 04 '23 at 07:46
  • @MatthieuM. The formatting got messed up. The problem was that I tried to implement a trait from another module and that does not work with a generic parameter because of the orphan rule. I'm using a macro now. – Florian Jan 05 '23 at 09:23
31

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() {}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
13

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.

Emoun
  • 2,297
  • 1
  • 13
  • 20
  • 1
    The first code block of your answer didn't work for me. Small tweak based on the crate documentation (https://docs.rs/duplicate/0.2.2/duplicate/): A,B should each be enclosed in their own [] square brackets. use duplicate::duplicate #[duplicate(name; [A]; [B])] impl T for name {... – Abhay Nainan Jun 20 '20 at 20:33
  • This is great! Just what I needed for the thing I'm working on. :) – Aurelia Peters Dec 16 '21 at 23:23
4

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:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366