How to efficiently encapsulate multiple where clauses and trait bounds

I'm implementing a math library and keep finding a common issue: I want users to be able to pass any combination of T and &T for lhs and rhs to std::ops binary traits (or ones that look like them).

I made a macro that given a user impl for &T, &T, it will generate (T, T), (&T, T), (T, &T) to solve the impl side of things.

From the consumption side, I'm stuck with writing out a bunch of nasty bounds.

use std::ops::Mul;

pub trait RhsMul<T>: Mul<T, Output=T> + for<'a> Mul<&'a T, Output=T> {}

pub struct Foo<T> where
T: RhsMul<T>,
for <'a> &'a T: RhsMul<T>
{
    data: T
}

impl<T> Foo<T> where
T: RhsMul<T>,
for <'a> &'a T: RhsMul<T>
{
    pub fn mul_1(self, rhs: Self) -> Self {
        Foo { data: self.data * rhs.data }
    }
    
    pub fn mul_2(self, rhs: &Self) -> Self {
        Foo { data: self.data * &rhs.data }
    }
    
    pub fn mul_3(&self, rhs: Self) -> Self {
        Foo { data: &self.data * rhs.data }
    }
    
    pub fn mul_4(&self, rhs: &Self) -> Self {
        Foo { data: &self.data * &rhs.data }
    }
}

I'm able to wrap the rhs into T and &T just fine with the RhsMul supertrait, but I'd like to join the where clauses into one alias. Something like:

alias clause Nasty<T> = 
  T: Mul<T, Output=T> + for <'a> Mul<&'a T, Output=T>,
  for <'a> &'a T: Mul<T, Output=T> + Mul<&'a T, Output=T>;

struct Foo<T> where Nasty<T>, T: Debug {
  data: T
}

impl<T> Foo<T> where Nasty<T>, T: Debug {
    fn mul_1(self, rhs: Self) -> Self {
        Foo { data: self.data * rhs.data }
    }
    
    fn mul_2(self, rhs: &Self) -> Self {
        Foo { data: self.data * &rhs.data }
    }
    
    fn mul_3(&self, rhs: Self) -> Self {
        Foo { data: &self.data * rhs.data }
    }
    
    fn mul_4(&self, rhs: &Self) -> Self {
        Foo { data: &self.data * &rhs.data }
    }
}

Is there anything like this in stable or even nightly Rust that addresses this?

You wish you had some form of implied bounds. They're not implemented yet as far as I know, I think it needs the new trait solver. (They also have some downsides I personally find significant).

It would definitely be an improvement to only declare the nasty bounds on the struct definition. In practice I'm implementing a bunch of type Foo, Bar, Baz etc. (and end users might as well). I guess one T and for<'a> &'a T clause per type isn't too bad.

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.