Reputation: 2810
I have a structure with two optional fields and would like to provide special macro to simplify instances creation.
The macro have to accept one required argument and three optional arguments. Last optional argument have to accept list of values. And I would like to use exactly one patter for that.
Finally I did that and everything works fine:
#[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);
}
The one thing makes me unhappy: the brackets in first: { Variant::OptionOne },
.
I tried to replace my pattern by (string: $string:expr, $($kind:ident : $val:expr),*$(,)*)
but it doesn't work for numebrs
anymore.
Is it possible to redefine pattern for the_macro
to allow first: Variant::OptionOne,
to be a valid while numbers
still can accept a list of items? Kind of brackets for numbers
doesn't matter but I can't replace list of things with just Vec or something similar.
P.S. I don't looking for a solution with multiple patterns.
Upvotes: 1
Views: 828
Reputation: 30061
There are two problems with your macro:
First it tries to do too much. Why initialize numbers
with numbers: (1, 3, 4)
while array literals use [
/]
and you can initialize a vector with only 4 extra characters?
Let's use the more natural notation numbers: vec![1, 3, 4]
. This has the advantage of letting your user initialize the field with an existing vector too.
Second, you need to use brackets around { Variant::OptionOne }
because you have used tt
for your value, but Variant::OptionOne
isn't one tt
, it's 3. If you change the pattern to a more natural expr
, you can use Variant::OptionOne
directly:
(string: $string:expr, $($kind:ident : $val:expr),*$(,)*) => {{
I would however advise you not to use such a macro. The usual way to deal with initialization of such structs in Rust is the builder pattern.
Upvotes: 1