Trait Alias in Stable Rust

I have written a playground script which demonstrates defining a struct implementing some standard traits and then defining a subtrait for use in function definitions to prevent the verbosity of those trait bounds. I.e. I have:

fn some_op(a: T, b: T, c:T) -> T
where T: RefFieldOps<T> { a + b - c }

instead of

fn some_op(a: T, b: T, c:T) -> T
where T: Add<T> + Sub<T> { a + b - c }

(Rust Playground)

The issue is that this isn't really what I want. I actually want the operations to work by reference.
I can get this work when explicitly stating the trait bounds:

fn some_op<T>(a: &T, b: &T, c: &T) -> T
where for<'a> &'a T : Add<&'a T, Output = T> + Sub<&'a T, Output = T>
{
    &(a + b) - c
}

(Rust Playground)

And ideally I would like a subtrait that summarises all of the cross operations, something like:

fn some_op<T>(a: &T, b: &T, c: &T) -> T
where 
    for<'a> 'a T: Add<&'a T, Output=T> + Add<'a T, Output=T> + Sub<&'a T, Output=T> + Sub<'a T, Output=T>
    for<'a> &'a T : Add<&'a T, Output = T> + Add<'a T, Output = T> + Sub<&'a T, Output = T> + Sub<'a T, Output=T>
{
    &(a + b) - c
}

An answer I received on Stack Overflow suggested the use of the experimental trait_alias (Rust Playground)

But I was hoping that this might exist already and I just didnt understand how to do it properly?

This works:

trait RefFieldOps<T>: Add<Output = T> + Sub<Output = T> + Sized {}
impl<'a, T: 'a> RefFieldOps<T> for &'a T where &'a T: Add<Output = T> + Sub<Output = T> {}

fn some_op<T>(a: &T, b: &T, c: &T) -> T
where
    for<'a> &'a T: RefFieldOps<T>,
{
    &(a + b) + c
}

I couldn't find a way to get rid of the generic for RefFieldOps despite it being kind of redundant.

Here's a messy playground. The idea was to make a shadow trait that references would implement, relying on the following implementations:

// for some lifetime 'a
&'a Base: Add<&'a Base, Output = Base>,
&'a Base: Add<    Base, Output = Base>,
    Base: Add<&'a Base, Output = Base>,
    Base: Add<    Base, Output = Base>,

It inherently has the following downsides:

  • You need four implementations of the base traits, not one
  • It forces lifetimes to be the same, which can sometimes bite you
  • Type-wise, it's opaque that &Base is the implementing type, so you can't trivially add the reference of an arithmetic result for example
    • This could be mitigated somewhat with Base: Copy

When playing with it, I also found you really had to hold the compiler's hand to get it to see all the implications when combining traits:

// The trait bounds were still quite messy without combining the two other
// traits, but when I combined them here...
trait AddSubRef: AddRef<Base = <Self as AddSubRef>::AsBase> + SubRef<Base = <Self as AddSubRef>::AsBase> {
    // ...I had to re-add all the bound even though the type equality
    // means they must always be true...
    type AsBase: Add<Self, Output = Self::AsBase> + Add<Self::AsBase, Output = Self::AsBase>
     + Sub<Self, Output = Self::AsBase> + Sub<Self::AsBase, Output = Self::AsBase>;
}

impl<T, Base> AddSubRef for T
where
    T: AddRef<Base = Base> + SubRef<Base = Base>,
    // ...and I had to repeat them here...
    Base: Add<T, Output = Base> + Add<Base, Output = Base>,
    Base: Sub<T, Output = Base> + Sub<Base, Output = Base>,
{
    // ...and now we need a distinct name to avoid ambiguity
    type AsBase = Base;
}

So it'd be less messy if you just made one trait for all the operations you needed from the start.

Like so (I also added the Base: Copy and made My: Copy).

Still not confident it's the best approach though.


The general idea behind getting trait bounds to be implicit is that you want them all to be

  • to be supertrait bounds, i.e., bounds on Self
  • to be associated type bounds

Thanks, I'll give this a try in coming days and see the results.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.