Here's a synthetic example of what I want:
macro_rules! define_enum {
($Name:ident { $($Variant:ident),* }) => {
pub enum $Name {
None,
$($Variant),*,
}
}
}
define_enum!(Foo { A, B });
This code compiles, but if add a comma to it:
define_enum!(Foo { A, B, });
// ^
The compilation fails. I can fix it with:
($Name:ident { $($Variant:ident,)* })
// ^
but then define_enum!(Foo { A, B }); fails,
How should I write a macro to handle both cases:
define_enum!(Foo { A, B });
define_enum!(Foo { A, B, });
Handle both cases
You can handle both cases by... handling both cases:
macro_rules! define_enum {
($Name:ident { $($Variant:ident,)* }) => {
pub enum $Name {
None,
$($Variant),*,
}
};
($Name:ident { $($Variant:ident),* }) => {
define_enum!($Name { $($Variant,)* });
};
}
define_enum!(Foo1 { A, B });
define_enum!(Foo2 { A, B, });
fn main() {}
We've moved the main implementation to the version that expects the trailing comma. We then added a second clause that matches the case with the missing comma and rewrites it to the version with a comma.
Make the comma optional
DK. points out an alternative, making the trailing comma itself optional.
This avoids the need to delegate from one implementation to the other.
Rust 1.32
You can use the ? macro repeater to write this and disallow multiple trailing commas:
($Name:ident { $($Variant:ident),* $(,)? }) => {
// ^^^^^
Previous versions
This allows multiple trailing commas:
($Name:ident { $($Variant:ident),* $(,)* }) => {
// ^^^^^
Change the line
($Name:ident { $($Variant:ident),* }) => {
to
($Name:ident { $($Variant:ident),* $(,)? }) => {
to add an optional comma at the end. This works in stable Rust / 2018 edition. This syntax also works for other separators like a semicolon.
Another option (if you're using an Incremental TT Muncher) is to use an optional capture of the delimiter + the remaining tokens. Using a slightly different example:
macro_rules! example {
($name:ident = $value:expr $(, $($tts:tt)*)?) => {
println!("{} = {}", stringify!($name), $value);
example!($($($tts)*)?);
};
($value:expr $(, $($tts:tt)*)?) => {
println!("{}", $value);
example!($($($tts)*)?);
};
() => {};
}
This macro can then be invoked as, for example:
example! { A = 1, "B", C = 3, "D" }
and the trailing comma can be either included or omitted.
Related
When trying to do
macro_rules! tipey {
(Vec<$pt: tt>) => { 2 };
(Vec<Option<$pt: tt>>) => { 3 };
($pt: tt) => { 1 };
}
macro_rules! structy {
(struct $i: ident {
$($p: ident: $pt: tt $(<$ppt: tt $(<$gt: tt> )? > )?),+
$(,)?
}) => {
const v: &[usize] = &[ $(tipey!( $pt $(<$ppt $(<$gt>)?>)?)),+ ];
};
}
structy!(
struct ContentDetails {
pattern: String,
fields: Vec<Option<String>>,
}
);
I get
How do I make this parse?
This problem is caused by your indiscriminate use of tt in your macros. This should be a last resort, as it can essentially match anything. The Rust compiler must disambiguate >> as part of a type v.s. expression based on the expected syntax. If you expect anything, then Rust must interpret >> as an operator.
Choose more appropriate metavariables such as ty (type), ident (identifier), path (fully specified path), etc.
This question already has answers here:
How do I match based on a dynamic variable?
(4 answers)
Closed 6 months ago.
I am trying to match an enum struct. This enum has different variants with different (or none) fields, such as:
enum Enum {
One {n : usize},
Two ,
}
The match block that I am trying to achieve looks like:
match m {
Enum::One{n: special_value} => {
println!("it is special! {}", special);
}
Enum::One{n} => {
println!("not special: {}", n);
}
Enum::Two => {
println!("not special");
}
}
If I define let m = Enum::One{n: 3} and let special_value = 8 the code prints it is special! 3. This should print not special: 3.
If I change the match block to (note that I changed the variable special_value variable to the literal 8:
match m {
Enum::One { n: 8 } => {
println!("it is special!");
}
Enum::One { n } => {
println!("not special: {}", n);
}
Enum::Two => {
println!("not special");
}
}
Now defining let m = Enum::One { n: 3 } it prints as desired not special: 3.
In my actual project case, I cannot use a literal to do the match the value comes from the function call as a variable.
For this porpouse I can use this match block (note the special case has been removed and replaced by an if clause inside the general case) :
match m {
Enum::One { n } => {
if n == special_value {
println!("it is special! {}", special_value);
} else {
println!("not special: {}", n);
}
}
Enum::Two => {
println!("not special");
}
}
Is it possible to use a variable inside the match, or am I forced to use the if clause? What is the reason?
yes. you can add if statements on the match arm without creating if block inside match arm block
does this works for you?
enum Enum {
One {
n: usize
},
Two
}
fn main() {
let special_value = 123;
let m = Enum::One { n: 123 };
match m {
Enum::One { n } if n == special_value => println!("it is special! {}", special_value),
Enum::One { n } => println!("not special: {}", n),
Enum::Two => {
println!("not special");
}
}
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=acfaae3976e7ca9fcf096c635a4daf98
works for me.
How do I check if a vector of enum has a certain member with values?
#[derive(PartialEq,Clone,Copy)]
enum TestResult {
Pass,
Fail {point: u8},
}
impl TestResult {
fn is_fail(&self) -> bool {
match *self {
TestResult::Fail{point:_} => true,
_ => false,
}
}
}
fn main() {
let v1 = vec![TestResult::Pass,
TestResult::Pass,
TestResult::Fail{point:50}];
if v1.contains(&TestResult::Pass) {
println!("result contains Pass");
}
if v1.iter().any(|&r| r.is_fail()) {
println!("result contains Fail");
}
}
This is working but is there a way to do this with Vec::contains()?
I want to check if TestResult::Fail is in a vector in the same way as for TestResult::Pass (no pattern matching. easy..)
There isn't really an easy way to do this, however we can make a simple macro to perform some pattern matching in the if statement.
/// Performs pattern matching contains on an IntoIter type
macro_rules! matches_any {
($iterable:expr, $($tokens:tt)+) => {{
let iter = ::std::iter::IntoIterator::into_iter($iterable);
iter.any(|x| matches!(x, $($tokens)+))
}};
}
The trick here is that we can use the matches! macro instead of a full match statement if all we want to know is if something matches a given pattern.
// Without matches!
match self {
TestResult::Fail {..} => true,
_ => false,
}
// With matches!
matches!(self, TestResult::Fail {..})
So now we can use this macro instead:
let v1 = vec![TestResult::Pass,
TestResult::Pass,
TestResult::Fail { point: 50 }];
if matches_any!(&v1, TestResult::Pass) {
println!("result contains Pass");
}
if matches_any!(&v1, TestResult::Fail {..}) {
println!("result contains Fail");
}
I'd like to create a custom macro similar to the standard dbg! macro, but with the option to use colors via the colored crate. dbg! usually prints something with the format of
[path_to_file:line_number] "symbol name" = "symbol value"
//[src/gallery/image_slot.rs:231] "my_integer_value_of_12" = "12"
How do I access the path/line number [path_to_file:line_number] so I can print it?
How do I access the symbol name of a variable? (i.e. print my_var given my_var = 12)
Use the file!, line!, and column! macros.
Use the stringify! macro.
If you go to the docs of the dbg! macro, you can click [src], which shows the implementation of dbg!, which is as follows:
macro_rules! dbg {
() => {
$crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!());
};
($val:expr $(,)?) => {
// Use of `match` here is intentional because it affects the lifetimes
// of temporaries - https://stackoverflow.com/a/48732525/1063961
match $val {
tmp => {
$crate::eprintln!("[{}:{}] {} = {:#?}",
$crate::file!(), $crate::line!(), $crate::stringify!($val), &tmp);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($($crate::dbg!($val)),+,)
};
}
Using that, we can easily create a similar colored_dbg! macro, with the colored crate as you suggested.
(I just picked random colors, for a simple example)
// colored = "2.0"
use colored::Colorize;
macro_rules! colored_dbg {
() => {
eprintln!("{}", format!("[{}:{}]", file!(), line!()).green());
};
($val:expr $(,)?) => {
match $val {
tmp => {
eprintln!("{} {} = {}",
format!("[{}:{}]", file!(), line!()).green(),
stringify!($val).red(),
format!("{:#?}", &tmp).blue(),
);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($(colored_dbg!($val)),+,)
};
}
You'd use it just like how you'd be able to use dbg!:
fn main() {
let my_var = 12;
colored_dbg!(&my_var);
let v = vec!["foo", "bar", "baz"];
let v = colored_dbg!(v);
}
Which outputs the following:
There are many question functions (hundreds), and each may have a different type. For each question I want to run a run_question function, which shows how long that function took, and print it's output.
I'm trying to shorten the following match expression with a Rust macro (writing run_question 100s of times does make the code rather long):
fn run_question<T: std::fmt::Display>(question_func: fn() -> T) {
let begin = Instant::now();
let output: T = question_func();
let elapsed_secs = begin.elapsed().as_micros() as f32 / 1e6;
println!("{}", output);
println!("{:.6}s taken", elapsed_secs);
}
fn q1() -> u8 { /* ... */ }
fn q2() -> u32 { /* ... */ }
fn q3() -> u64 { /* ... */ }
fn q4() -> String { /* ... */ }
fn main() {
// ...
match question_num {
1 => run_question(q1), 2 => run_question(q2), 3 => run_question(q3), 4 => run_question(q4),
_ => {
println!("Question doesn't exist.");
},
}
}
I have no experience in writing macros, and attempted the following which doesn't exactly work. It gives the error:
error: variable 'question_num' is still repeating at this depth
I'm rather stumped too how I can print the Question doesn't exist. as a default case.
#[macro_export]
macro_rules! run_questions {
( $chosen_question: expr, $( $question_num: expr, $question_mod: expr ), * ) => {
{
if $chosen_question == $question_num {
run_question($question_mod::solve);
}
}
};
}
The way I'd like to use it, is (or anything just as short is fine as well):
run_questions!(question_num, 1, q1, 2, q2, 3, q3, 4, q4);
I read a bit of the Rust book, but there aren't exactly that many examples of macros.
How would I go about doing this?
Rather than many if statements, I just reproduced the match statement
with a repetition $( ... )* for all the available branches.
It seems to behave like the extensive match expression.
macro_rules! run_questions {
( $chosen_question: expr, $( $question_num: expr, $question_mod: expr ), * ) => {
match $chosen_question {
$($question_num => run_question($question_mod),)*
_ => {
println!("Question doesn't exist.");
}
}
};
}
The error message explained:
macro_rules! run_questions {
($chosen_question: expr, $($question_num: expr, $question_mod: expr),*) => {{
In the above pattern you have a repetition with the * operator that involves variables $question_num and $question_mod
if $chosen_question == $question_num {
run_question($question_mod::solve);
}
In the corresponding code, you can't use $question_num and $question_mod directly: since they are repeated they potentially have more than one value and which one should the compiler use here? Instead, you need to tell the compiler to repeat the block of code that uses these variables. This is done by surrounding the repeated code block with $() and adding the * operator:
$(if $chosen_question == $question_num {
run_question($question_mod::solve);
})*
Although as pointed out by #prog-fh's answer, better to use a match in the macro, same as in the straight code:
match $chosen_question {
$($question_num => run_question ($question_mod::solve),)*
_ => println!("Question doesn't exist.")
};