0

I have a lot of lengthy and repetitive trait constraint chains in my implementations that I want to replace. Type aliasing for traits looks great but it is still an unstable feature.

I tried using something like this

trait NumericTrait : Float + FromPrimitive + ToPrimitive + RealField + Copy + Clone + Debug + 'static {}

However this leads to issues since NumericTrait is not explicitly implemented for the types I am concerned with. Rather, all of the base traits are.

I am now thinking macros is the way to go, so I tried the following:

macro_rules! numeric_float_trait {
    () => {
        Float + FromPrimitive + ToPrimitive + RealField + Copy + Clone + Debug + 'static
    };
}

struct SomeStruct;

impl<T> SomeStruct 
where
    T: numeric_float_trait!()
{
    fn do_things(num: T) {
        println!("I got this num {:?}", num);
    }
}

However the syntax is incorrect and the compiler will not accept it. How do I achieve this desired functionality of essentially pasting a list of traits via a macro?

Hurricane Development
  • 2,449
  • 1
  • 19
  • 40
  • With the `NumericTrait` approach, you can add a generic implementation for all `T` that satisfy the trait bounds. This requires to write the trait bounds a second time, but that's acceptable in my opinion. – Sven Marnach Sep 09 '19 at 12:39
  • Possible duplicate of [Is there a way to combine multiple traits in order to define a new trait?](https://stackoverflow.com/questions/26983355/is-there-a-way-to-combine-multiple-traits-in-order-to-define-a-new-trait) – Sven Marnach Sep 09 '19 at 13:06

1 Answers1

1

You can declare the macro that creates a trait you wanted like following:

macro_rules! declare_trait {
    ($trait_type:ident,$trait_name:ident) => {
        trait $trait_name:
            $trait_type
            + num::FromPrimitive
            + num::ToPrimitive
            + alga::general::RealField
            + ::std::marker::Copy
            + ::std::clone::Clone
            + ::std::fmt::Debug
            + 'static
        {
        }

}

In your macro you need to define the implementation of any T that has the following traits given:

impl<T> $trait_name for T where
    T: $trait_type
      + num::FromPrimitive
      + num::ToPrimitive
      + alga::general::RealField
      + ::std::marker::Copy
      + ::std::clone::Clone
      + ::std::fmt::Debug
      + 'static
{
}

Now you only need to call this macro to declare the trait as you desire:

declare_trait!(Float, NumericFloatTrait); // Float is type and NumericFloatTrait is the name here

After you declare your trait you can use it anywhere you like:

struct SomeStruct;

impl SomeStruct {
    fn do_things_with_float<T: NumericFloatTrait>(num: T) {
        println!("I got this float {:?}", num);
    }
}

Playground

Akiner Alkan
  • 6,145
  • 3
  • 32
  • 68
  • This is what I tried (albeit without a macro) in my first example above. This doesn't work for me because num does not actually implement the `NumericFloatTrait`. It implements all of the sub-traits only... – Hurricane Development Sep 09 '19 at 07:14
  • Since we have declared `NumericFloatTrait` it is expected that num does not implement that trait. I do not understand what you commented out. Can you please explain it more or edit the question in the comprehensive way – Akiner Alkan Sep 09 '19 at 07:21
  • @HurricaneDevelopment, Updated the answer as your needs. Please check again – Akiner Alkan Sep 09 '19 at 07:54
  • @Websterix at least you have to make sure you use `::std::marker::Copy`, `::std::clone::Clone`, etc. in your macro otherwise the usage of this macro will lead to undesired consequences (i.e. locally defined `Copy` or `Clone`). Moreover, you could use a custom syntax in your matching, like: `(trait $trait_name:ident: $trait_type: ident) => { ... }` which could be used as: `declare_trait!{ trait NumericFloatTrait: Float }` which IMO is a slightly bit more self explanatory. But you could come up with a better syntax. – Peter Varo Sep 09 '19 at 08:18
  • !!! `std` could still be redefined !!! https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c32578def81c4b5fd4b4837c8151c9b2 – Peter Varo Sep 09 '19 at 12:44
  • Your second example is more along the lines of what I am trying to do, however it is a bit more restricting than I would like. For example, if I want to have additional generics. For the first solution, the issue is that in implementation, T does not actually implement `$type_name`, rather it implements all of the subtraits. – Hurricane Development Sep 09 '19 at 16:04