我尝试定义宏,用于定义一堆常量和一个映射。我想用法会是这样的:
options_filter_consts!(
MEAL_TYPE => "meal_type" // filter_id
options { // filter options (const_name => const_value)
MEAL_NO => "meal_no"
BREAKFAST => "breakfast"
FULLBOARD => "fullboard"
HALFBOARD => "halfboard"
}
);
然后它会产生以下代码:
// Filter ID
const MEAL_TYPE:&str = "meal_type";
// Filter Options
const NO_MEAL :&str = "no_meal";
const BREAKFAST:&str = "breakfast";
const FULLBOARD:&str = "fullboard";
const HALFBOARD:&str = "halfboard";
// Option indexes
const NO_MEAL_IDX :usize = 0;
const BREAKFAST_IDX:usize = 1;
const FULLBOARD_IDX:usize = 2;
const HALFBOARD_IDX:usize = 3;
// Mapping indexes -> options
// Naming: <filter_id> + _MAPPING
const MEAL_TYPE_MAPPING:[&str; 4] = [
/* NO_MEAL_IDX: */ NO_MEAL,
/* BREAKFAST_IDX: */ BREAKFAST,
/* FULLBOARD_IDX: */ FULLBOARD,
/* HALFBOARD_IDX: */ HALFBOARD,
];
这些常量将在主函数中使用,例如:
fn main() {
println!("filter_id: {}", MEAL_TYPE);
println!("mapping_len: {}", MEAL_TYPE_MAPPING.len());
println!("mapping_item_value: {}", MEAL_TYPE_MAPPING[MEAL_NO_IDX]);
println!("item_value: {}", BREAKFAST);
}
我开始写宏:
macro_rules! options_filter_consts {
($filt_const:ident => $filt_value:expr, options { $($name:ident => $value:expr)+ }) => {
const $filt_const: &str = $filt_value;
$(
const $name : &str = $value;
)+
const IOTA:usize = 0; // how to increment inside marco ?
$(
paste! {
const [<$name _IDX>]:usize = IOTA;
}
)+
};
}
但是我遇到了一些错误,例如我无法增加 IOTA 常量值并陷入困境。
目前,Rust 宏不支持宏内部的任何代码执行。 宏语言基于模式匹配和变量替换,并且仅评估宏。
在这种情况下,可以用宏递归来覆盖某些情况。例如计数。
对于您的情况,您也可以使用类似的方法。
/// A macro that generates a sequence of constants with incrementing index values.
macro_rules! increment_consts {
($name:ident, $value:expr) => {
paste! {
const [<$name _IDX>] : usize = $value;
}
};
($name:ident, $value: expr, $($rest:ident, $new_value:expr),*) => {
paste! {
const [<$name _IDX>] : usize = $value;
}
increment_consts!($($rest, $value + 1),*);
};
}
/// A macro that generates a sequence of constants with incrementing index values.
/// The `create_consts!` macro takes a list of identifiers as input and generates
/// a sequence of constants with incrementing index values for each identifier.
macro_rules! create_consts {
($($rest:ident),* $(,)?) => {
increment_consts!($($rest, 0),*);
};
}
然后在
main.rs
中让我们使用一个新的宏。
fn main() {
create_consts!(A, B, C, D);
}
让我们用以下方式扩展宏:
cargo +nightly rustc --profile=check -- -Zunpretty=expanded
正在工作;我们正是需要的。
...
fn main() {
const A_IDX: usize = 0;
const B_IDX: usize = 0 + 1;
const C_IDX: usize = 0 + 1 + 1;
const D_IDX: usize = 0 + 1 + 1 + 1;
}
让我们将其添加到您的解决方案中。
use paste::paste;
/// A macro that generates a sequence of constants with incrementing index values.
macro_rules! increment_consts {
($name:ident, $value:expr) => {
paste! {
const [<$name _IDX>] : usize = $value;
}
const _ARR_LEN: usize = $value;
};
($name:ident, $value: expr, $($rest:ident, $new_value:expr),*) => {
paste! {
const [<$name _IDX>] : usize = $value;
}
increment_consts!($($rest, $value + 1),*);
};
}
/// A macro that generates a sequence of constants with incrementing index values.
/// The `create_consts!` macro takes a list of identifiers as input and generates
/// a sequence of constants with incrementing index values for each identifier.
macro_rules! create_consts {
($($rest:ident),* $(,)?) => {
increment_consts!($($rest, 0),*);
};
}
macro_rules! options_filter_consts {
($filt_const:ident => $filt_value:expr, options { $($name:ident => $value:expr)+ }) => {
const $filt_const: &str = $filt_value;
$(
const $name : &str = $value;
)+
create_consts!($($name),+);
paste! {
const [<$filt_const _MAPPING>] : [&str; _ARR_LEN + 1] = [$($name,)+];
}
};
}
fn main() {
options_filter_consts!(
MEAL_TYPE => "meal_type", // filter_id
options { // filter options (const_name => const_value)
MEAL_NO => "meal_no"
BREAKFAST => "breakfast"
FULLBOARD => "fullboard"
HALFBOARD => "halfboard"
}
);
println!("{}", HALFBOARD_IDX);
println!("{}", MEAL_NO_IDX);
println!("{}", FULLBOARD_IDX);
println!("{:?}", MEAL_TYPE_MAPPING);
}
再次展开宏,看看这次我们有什么:
...
fn main() {
const MEAL_TYPE: &str = "meal_type";
const MEAL_NO: &str = "meal_no";
const BREAKFAST: &str = "breakfast";
const FULLBOARD: &str = "fullboard";
const HALFBOARD: &str = "halfboard";
const MEAL_NO_IDX: usize = 0;
const BREAKFAST_IDX: usize = 0 + 1;
const FULLBOARD_IDX: usize = 0 + 1 + 1;
const HALFBOARD_IDX: usize = 0 + 1 + 1 + 1;
const _ARR_LEN: usize = 0 + 1 + 1 + 1;
const MEAL_TYPE_MAPPING: [&str; _ARR_LEN + 1] =
[MEAL_NO, BREAKFAST, FULLBOARD, HALFBOARD];
{ ::std::io::_print(format_args!("{0}\n", HALFBOARD_IDX)); };
{ ::std::io::_print(format_args!("{0}\n", MEAL_NO_IDX)); };
{ ::std::io::_print(format_args!("{0}\n", FULLBOARD_IDX)); };
{ ::std::io::_print(format_args!("{0:?}\n", MEAL_TYPE_MAPPING)); };
}