How can I create a Rust macro that only accepts certain number literals in a scalable manor?
Ideally, such a solution meets the following requirements:
You can create a macro where each arm accepts a specific number as shown below. However I also want to perform computation on the allowed numbers, leading to lots of code duplication since I'm dealing with hundreds of allowed numbers.
macro_rules! my_macro {
(1) => {
1
};
(2) => {
2
};
(3) => {
3
};
}
compile_error!
The compile_error!
macro reports the error inside my_macro
, not on the arguments to my_macro!
. This can be very unintuitive.
macro_rules! my_macro {
($num: literal) => {
// if number is invalid
compile_error!("number is invalid")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // error is reported here
// else
$num
};
}
// Let's say 34 is an invalid number
my_macro!(34)
I'm trying to emulate a behavior with TypeScript's type system where you can create a type that limits the specific numbers a function accepts.
type AllowedNumbers = 1 | 2 | 3
const myFunc = (num: AllowedNumbers) => {
// ...
}
You can panic in a const to create a compile-time error dependent on a literal.
macro_rules! one_to_ten {
($n:literal) => {
const _: () = if $n < 1 || $n > 10 {
panic!("was not between 1 and 10")
};
};
}
This produces the following error.
error[E0080]: evaluation of constant value failed
--> src/lib.rs:10:5
|
10 | one_to_ten!(20);
| ^^^^^^^^^^^^^^^ the evaluated program panicked at 'was not between 1 and 10', src/lib.rs:10:5
|
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `one_to_ten` (in Nightly builds, run with -Z macro-backtrace for more info)
Your idea to use compile_error
doesn't work anyway since there's no way to conditionally compile it based on the value of a literal besides using multiple branches.
I think this is as good as you'll get with declarative macros. You could create better error messages if you write a procedural macro.