How can I match the type of an expression in a Rust macro?

This is just pseudo code:

macro_rules! attribute { $e: expr<f32> => { /* magical float stuff */ }; $e: expr<i64> => { /* mystical int stuff */ }; }; 

I would like to have a different extended macro depending on the type that I passed to the macro.

Here's how it works in C ++

 template <typename T> struct Attribute{ void operator(T)() {} }; template <> struct Attribute<float> { void operator(float)(float) { /* magical float stuff */ } }; template <> struct Attribute<long> { void operator()(long) { /* mystical int stuff */ } } 
+6
source share
2 answers

Rust macros cannot do this. Macros work at the syntactical level, and not at the semantic level. This means that although the compiler knows that it has an expression (syntax), it does not know what type of expression value (semantic) is at the time the macro expands.

A workaround would be to pass the expected type to the macro:

 macro_rules! attribute { ($e:expr, f32) => { /* magical float stuff */ }; ($e:expr, i64) => { /* mystical int stuff */ }; } fn main() { attribute!(2 + 2, i64); } 

Or, more simply, define a few macros.


If you want to do static (compilation) mailing based on an expression type, you can use traits. Define the trait using the necessary methods, then implement the trait for the types you need. You can implement a trait for any type (including primitives and types from other libraries) if the impl block is in the same box as the trait definition.

 trait Attribute { fn process(&self); } impl Attribute for f32 { fn process(&self) { /* TODO */ } } impl Attribute for i64 { fn process(&self) { /* TODO */ } } macro_rules! attribute { ($e:expr) => { Attribute::process(&$e) }; } fn main() { attribute!(2 + 2); } 

Note. You can also write $e.process() in a macro $e.process() , but then the macro can call the unbound process method.

+11
source

As already explained, you cannot expand differently depending on the type of expr . But as a workaround, you can use any module and try to drop from the any sign:

 use std::any::Any; macro_rules! attribute { ( $e:expr ) => { if let Some(f) = (&$e as &Any).downcast_ref::<f32>() { println!("`{}` is f32.", f); } else if let Some(f) = (&$e as &Any).downcast_ref::<f64>() { println!("`{}` is f64.", f); } else { println!("I dunno what is `{:?}` :(", $e); } }; } fn main() { attribute!(0f32); attribute!(0f64); attribute!(0); } 

Output:

 `0` is f32. `0` is f64. I dunno what is `0` :( 
+1
source

All Articles