How to enforce that a macro `ty` argument implements a trait at compile time?


Keywords:macros 


Question: 

I want to write a macro like this:

macro_rules! a {
    ( $n:ident, $t:ty ) => {
         struct $n {
             x: $t
         }
    }
}

But $t should implement Add, Sub and Mul traits. How can I check it at compile-time?


1 Answer: 

First, solve the problem without macros. One solution is to create undocumented private functions that will fail compilation if your conditions aren't met:

struct MyType {
    age: i32,
    name: String,
}

fn __assert_send()
where
    MyType: Send,
{}

fn __assert_sync()
where
    MyType: Sync,
{}

Then, modify the simple solution to use macros:

macro_rules! a {
    ($n:ident, $t:ty) => {
        struct $n {
            x: $t
        }

        impl $n {
            fn __assert_add()
            where
                $t: std::ops::Add<$t, Output = $t>
            {}

            fn __assert_mul()
            where
                $t: std::ops::Mul<$t, Output = $t>
            {}
        }
    }
}

a!(Moo, u8);
a!(Woof, bool);

fn main() {}

I would then trust in the optimizer to remove the code at compile time, so I wouldn't expect any additional bloat.

Major thanks to Chris Morgan for providing a better version of this that supports non-object-safe traits.