我有一个带有两个可选字段的结构,并想提供特殊的宏来简化实例的创建。
宏必须接受一个必需的参数和三个可选的参数。最后一个可选参数必须接受值列表。我想为此使用一种模式。
最后,我做到了,一切正常:
#[derive(Debug)]
pub enum Variant {
OptionOne,
OptionTwo,
}
#[derive(Debug)]
pub struct TheStruct{
string: String,
first: Option<Variant>,
second: Option<Variant>,
numbers: Vec<u32>,
}
impl TheStruct {
// doesn't matter
}
#[doc(hidden)]
#[macro_export]
macro_rules! the_macro_int {
($the_struct:ident, numbers, { $($w:expr),*$(,)*}) => {
$($the_struct.numbers.push($w);)*
};
($the_struct:ident, first, {$w:expr}) => {
$the_struct.first = Some($w);
};
($the_struct:ident, second, {$w:expr}) => {
$the_struct.second = Some($w);
};
}
#[macro_export]
macro_rules! the_macro {
(string: $string:expr, $($kind:ident : $val:tt),*$(,)*) => {{
let mut the_struct = $crate::TheStruct{
string: $string,
first: None,
second: None,
numbers: std::vec::Vec::new(),
};
$($crate::the_macro_int!(the_struct, $kind, $val);)*
the_struct
}};
}
fn main() {
let the_struct = the_macro!(
string: "Hello".to_owned(),
first: { Variant::OptionOne },
numbers: (1, 3, 4),
);
println!("the_struct:{:?}", the_struct);
}
Playground
一件事使我不高兴:
first: { Variant::OptionOne },
中的括号。我试图用
(string: $string:expr, $($kind:ident : $val:expr),*$(,)*)
替换我的模式,但是它不再适用于numebrs
。是否可以重新定义
the_macro
的模式,以使first: Variant::OptionOne,
有效,而numbers
仍可以接受项目列表? numbers
的括号并不重要,但是我不能仅用Vec或类似内容替换列表。P.S.我不是在寻找具有多种模式的解决方案。
最佳答案
您的宏有两个问题:
numbers
/numbers: (1, 3, 4)
的情况下使用[
初始化]
,而您只能用4个额外的字符来初始化矢量?让我们使用更自然的表示法
numbers: vec![1, 3, 4]
。这具有使您的用户也可以使用现有矢量初始化字段的优点。 { Variant::OptionOne }
周围使用方括号,因为您使用tt
作为值,但是Variant::OptionOne
并不是一种tt
,而是3。如果将模式更改为更自然的expr
,则可以直接使用Variant::OptionOne
:(string: $string:expr, $($kind:ident : $val:expr),*$(,)*) => {{
(Permalink to the playground)
但是,我建议您不要使用这样的宏。在Rust中处理此类结构初始化的常用方法是builder pattern。
关于rust - 具有三个可选参数的宏,其中一个必须接受值列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61261661/