This is just pseudocode:
macro_rules! attribute {
$e: expr<f32> => { /* magical float stuff */ };
$e: expr<i64> => { /* mystical int stuff */ };
};
I would like to have a differently expanded macro depending on the type that I passed to the macro.
This is how it would work 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 */ }
}
Rust macros aren't able to do that. Macros operate at the syntactic level, not at the semantic level. That means that although the compiler knows it has an expression (syntax), it doesn't know what the type of the expression's value (semantic) is at the moment the macro is expanded.
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 multiple macros.
If you want to do static (compile-time) dispatch based on the type of an expression, you can use traits. Define a trait with 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 crate 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 could also write $e.process()
in the macro's body, but then the macro might call an unrelated process
method.
As already explained, you cannot expand differently depending on the type of an expr
. But as a workaround, you can use the any
module and try to downcast from the Any
trait:
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);
}
Displays:
`0` is f32.
`0` is f64.
I dunno what is `0` :(