Alberto Centelles
Alberto Centelles

Reputation: 1253

Rust traits on newtypes

Let's say I have defined a Group trait with all the group operations. Would it be possible to create a wrapper AGroup over Group without having to manually derive all its operations?

Basically, I'd like to have this:

#[derive (Copy, Debug, Clone, Eq, PartialEq, Group)] // How could I derive Group here as well?
struct AGroup<G: Group>(G);

without all this boilerplate:

impl<G: Group> Add for AGroup<G>{
    type Output = AGroup<G>;
    fn add(self, other: Self) -> Self {
        AGroup(self.0 + other.0)
    }
}

impl<G: Group> Neg for AGroup<G>{
    type Output = AGroup<G>;
    fn neg(self) -> Self {
        AGroup(-self.0)
    }
}
...

Upvotes: 7

Views: 2674

Answers (2)

trent
trent

Reputation: 27905

It's not possible in Rust today (as of 1.50) or for the foreseeable future.

You could write a custom derive macro for Group. This amounts to writing code that generates the boilerplate, instead of writing the boilerplate itself. It might be worthwhile if you're going to put #[derive(Group)] on multiple types. But if you only want automatic implementations for AGroup<G>, it'll probably be much shorter and simpler just to write the boilerplate.

The name of the feature you really want is "delegation". It's been RFCed a couple of times but never accepted (IMO both RFCs have tried to cover too many different use cases and turned out unwieldy). There are concerns about what syntax is the best, how granular delegation should be, and how easily it might be abused to create brittle "inheritance-like" relationships between data structures.

For now, if you cannot find a different design that doesn't require delegation (such as that suggested in Netwave's answer), you should probably bite the bullet and write the delegating impls yourself. There are crates such as delegate and ambassador that can make certain delegation patterns easier, but I'm not sure if either of them exactly fit what you're trying to do (more evidence that everybody wants a slightly different form of "delegation").

Related questions

On implementing Deref, which is occasionally suggested as an alternative to delegation:

Upvotes: 4

Netwave
Netwave

Reputation: 42698

You could also just create a method exposing the inner object interface:

pub trait Group {
    fn foo(self: &Self){
        println!("Called from group")
    }
}

#[derive (Copy, Debug, Clone, Eq, PartialEq)] // How could I derive Group here as well?
pub struct AGroup<G: Group>(G);

impl<G: Group> AGroup<G> {
    pub fn inner(&self) -> &impl Group {
        &self.0
    }
}

pub struct Test {}
impl Group for Test {}



fn main( ) {
    let a = AGroup( Test {} );
    a.inner().foo();
}

Playground

Upvotes: 1

Related Questions