Creating alias for bounds?


#1

Per https://github.com/rust-lang/rust/issues/8634, I can sort-of create an alias for some traits:

pub trait Addable: Add<Output=Self> + Debug + Sized + Clone {}
impl<T: Add<Output=T> + Debug + Sized + Clone> Addable for T {}

pub struct Showoff<T> { a: T, b: T }

impl<T: Addable> Showoff<T> {
  pub fn sum(&self) -> T { self.a.clone() + self.b.clone() }
}

fn main() {
  let showoff = Showoff { a: 1, b: 2 };
  println!("sum: {}", showoff.sum());
}

This works pretty good. Essentially, T: Addable currently is an alias for T: Add<Output=T> plus some other stuff.

The need to clone bothers me, though: as an example, it seems like a Showoff<isize> is doing some unnecessary copies. What I’d like is for T: Addable to also be an alias for &T: Add<Output=T>. Then I wouldn’t have to clone:

pub trait Addable<'a>: Add<Output=Self> + Debug + Sized
    where Self: 'a, &'a Self: Add<Output=Self> {}
impl<'a, T: Add<Output=T> + Debug + Sized> Addable<'a> for T
    where T: 'a, &'a T: Add<Output=T> {}

pub struct Showoff<T> { a: T, b: T }

impl<'a, T: Addable<'a>> Showoff<T> {
  pub fn sum(&'a self) -> T { &self.a + &self.b }
}

However, rustc doesn’t compile, complaining that impl Showoff doesn’t satisfy &T: Add. It seems like T: Addable carries the T: Add<...> bounds with it, but not the &T: Add<...> bounds, forcing me to repeat it on all its impls:

impl<'a, T: Addable<'a>> Showoff<T> where T: 'a, &'a T: Add<Output=T> {...}

which somewhat negates the reason I’m trying to create an alias in the first place.

Does anyone have an idea how to accomplish what I’m trying to do here? It feels like implementation bounds on a trait should carry all the bounds of that trait, but perhaps I’m missing something. It’s not a huge deal: basically just more typing, but any help or insight here would be appreciated.


#2

It looks like a bug, go ahead an file it on the issue tracker. The where bounds are checked on the blanket impl, but the compiler doesn’t seem to see them in implementation. You could even remove pub fn sum, leaving the impl empty, at it still fails to compile.

Anyway, I think the trait definition would look more clearly that way:

pub trait Addable: Add<Output=Self> + Debug + Sized
where
    for<'a> &'a Self: Add<Output=Self>,
    for<'a> &'a Self: Add<&'a Self, Output=Self> {}

That way the lifetime parameter doesn’t pollute the trait definition. It results in the same compiler error though.

Also, don’t worry about cloning isizes. It’s going to be optimized out. The clone will be an issue only when using some bigger T. Even if you’re cloning a struct with multiple fields, it shouldn’t matter, since the cpu has to look at the whole struct anyway. I think it may be a problem only if clone allocates (eg. bignum).


#3

I believe it’s this issue: https://github.com/rust-lang/rust/issues/20671


#4

Thanks, guys! Yeah, it does seem that this is a case of issue 20671. I’ll keep my eye on that one, and just duplicate some code for now.

Thanks @krdln for the tips about HRTB and clone optimizations! That will help me out.