0

I would like to make something like the following work:

use std::ops::Add;

trait CanBeAdded: Sized where f64: Add<Self> {}

fn add2<X: CanBeAdded>(x: X) {}

fn main() {}

The above fails to compile:

error[E0277]: the trait bound `f64: std::ops::Add<X>` is not satisfied
 --> src/main.rs:5:1
  |
5 | fn add2<X: CanBeAdded>(x: X) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::ops::Add<X>` is not implemented for `f64`
  |
  = help: consider adding a `where f64: std::ops::Add<X>` bound
  = note: required by `CanBeAdded`

Playground link

I'm trying to assert the existence of certain impls via a trait: i.e. X: CanBeAdded implies f64: Add<X>. While I can add the bounds to the where clause of the function like this:

fn add2<X>(x: X) where f64: Add<X> { }

I have many of them so it gets unwieldy and I would prefer to not repeat the bounds over and over. Is there a good solution to this? For example, is it possible to include a macro that expands to a series of where clauses?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
yong
  • 3,583
  • 16
  • 32
  • Duplicate of [Is there a way to combine multiple traits in order to define a new trait](http://stackoverflow.com/q/26983355/155423)? – Shepmaster Jan 20 '17 at 03:33
  • Hmm, it's different because it involves a second type, `f64` -- it involves the relationship between two or more types. Should I make that more explicit? – yong Jan 20 '17 at 03:45
  • @DK Adding the line `impl CanBeAdded for X where f64: Add {}` doesn't help -- what should the `imp` look like? https://play.rust-lang.org/?gist=59b0932a780fdcaf21848de9d310621a&version=nightly&backtrace=0 – yong Jan 20 '17 at 05:08
  • @yong Ah, I see now. I was wrong. The problem is that the compiler is not implemented such that it can use the information in the trait's where clause to infer `f64: Add` in the function. You can have the constraint in place, but you can't use it to force a constraint on a different type. The simplest workaround I know of would be to implement a `fn add_to_f64(self, f64)` to the `CanBeAdded` trait directly, and not to try and use `+` itself. – DK. Jan 20 '17 at 05:13

1 Answers1

0

It's actually quite unlikely that a random type X is such that f64: Add<X>.

Given that f64 is a built-in, the only module which can define implementations of Add for f64 is the one where Add is defined. We can check the listed implementations here:

  • impl Add<f64> for f64
  • impl<'a> Add<&'a f64> for f64

And that's all.


Still, if that is all that you want, you can define the trait differently.

First, we define a new marker trait. It's pretty uninteresting by itself as it has absolutely no method:

trait ReverseAdd<T> { type Output; }

Then, we add a blanket implementation, to automatically implement the trait for any type that implements Add, but with the relationship reversed:

impl<T, U> ReverseAdd<T> for U where T: Add<U> {
    type Output = <T as Add<U>>::Output;
}

And finally, we use it as our bound:

fn add2<X: ReverseAdd<f64>>(x: X) {
}
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722