How to concat two variables to create an identifier in a declarative macro? [duplicate] - rust

This question already has answers here:
Is it possible to declare variables procedurally using Rust macros?
(4 answers)
Closed yesterday.
I want to write a macro to support parameterized tests, and got the following code from AI, but got errors on one line:
#[macro_export]
macro_rules! parameterize {
($name:ident, $params:pat, {$($test_name:ident : ($($args:expr),*)),*}, $test_body:block) => {
$(
fn $name_\$test_name() { //<========= this line
let $params = ($($args),*);
$test_body
}
)*
};
}
parameterize! {
should_be_added,
(expected, a, b),
{
positive: (3, 1, 2),
zero: (0, 0, 0),
negative: (-5, -2, -3),
large_numbers: (999999, 444444, 555555)
},
{
println!("a={}, b={}, expected={}", a, b, expected);
}
}
fn main() {
positive();
negative();
}
If I change fn $name_\$test_name() to fn $test_name() , it works well.
So I want to know how to concatenate the two variables $name $test_name to output one string. For example, if $name is foo and $test_name is bar, how to output foo_bar?

There is a crate called paste, that provides a macro for concatenating tokens inside declarative macros.
To use it, wrap your entire result in the paste! macro, and then you can concatenate tokens by placing them between [< and >] with spaces to separate.
use paste::paste;
#[macro_export]
macro_rules! parameterize {
($name:ident, $params:pat, {$($test_name:ident : ($($args:expr),*)),*}, $test_body:block) => {
paste! {
$(
fn [<$name _ $test_name>]() { // <- special syntax used by paste!
let $params = ($($args),*);
$test_body
}
)*
}
};
}
Using the remainder of the code you've provided, this would generate functions should_be_added_positive and should_be_added_negative.

Related

Construct ident from string/int in declarative macro

I have a dozen crates all with two functions part1() and part2(). For each of those, I want to print the return value of both functions. To not repeat myself, I wrote a little macro, so my main crate's main() looks like this:
fn main() {
macro_rules! print_day {
($day:ident) => {
let day = stringify!($day).replace('d', "");
println!("Day {day} Part 1: {}", $day::part1());
println!("Day {day} Part 2: {}", $day::part2());
};
}
print_day!(d01);
print_day!(d02);
print_day!(d03);
// list goes on..
}
Is it possible to construct an ident from a string or int in a declarative macro, so I could instead do like below? All I've found is either for proc macros, using additional packages or the other way round.
fn main() {
macro_rules! print_day {
($day:expr) => {
let daystr = format!("{:02}", $day);
let dayident = ???;
println!("Day {daystr} Part 1: {}", $dayident::part1());
println!("Day {daystr} Part 2: {}", $dayident::part2());
};
}
for day in 0..=num_days {
print_day!(day);
}
}
Using concat_idents you can glue identifiers together, but unfortunately it does not (yet?) allow non-identifiers such as numbers.
#![feature(concat_idents)]
fn m_1() {
println!("Hi");
}
macro_rules! m {
($n:ident) => {
concat_idents!(m, $n)();
};
}
fn main() {
// underscore is needed to make it into an identifier
m!(_1);
}
So we need to list the crates manually, just like we list the functions manually.
I think something like below should work, but it does not.
macro_rules! call_all {
(#funs $crat:ident $($fun:ident)+) => {{
$($crat::$fun();)*
}};
($($crat:ident)+ # $($fun:ident)+) => {{
// error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
$(call_all!(#funs $crat $($fun)*);)*
// ^^^^^^
}};
}
fn main() {
call_all!(tokio rand # m_1 m_2)
}
You can use the seq-macro crate to simplify that:
fn main() {
seq_macro::seq!(day in 1..=25 {
println!("Day {} Part 1: {}", day, d~day::part1());
println!("Day {} Part 2: {}", day, d~day::part2());
});
}
Sadly, it does not support formatting with leading zeroes (01, 02, ...) or using a variable for the number of days. The first could be fixed with a custom proc macro, the second could not.

Writing a rust macro repetition that may or may not initialize a struct field

I'm trying to write a macro that lists a number of struct fields, but conditionally only creates initializer code from some fields in the list. Specifically, that might look something like this:
#[test]
fn test() {
#[derive(PartialEq, Debug)]
struct Foo {
bar: usize,
}
let a = Foo {
bar: 0,
};
let b = test!(Foo {
bar: 0,
#[nope] baz: 0,
});
assert_eq!(a, b);
}
Foo has no field baz, and the #[nope] should tell the macro to just do nothing for that field; the real macro would use baz in one but not another location, and there are also other "attributes" that need to be handled.
This is the base-line macro that accepts the invocation, but doesn't ignore baz:
macro_rules! test {
(
$struct:ty {
$($(#[$modifier:ident])? $field:ident: $value:expr,)*
}
) => {
{
// don't mind this syntax workaround
type X = $struct;
X {
$($field: $value,)*
}
}
};
}
Now the trick I know to have different rules for different repetitions is to delegate each repetition to a helper rule. Here's the same macro splitting the regular and nope variations:
macro_rules! test {
(
$struct:ty {
$($(#[$modifier:ident])? $field:ident: $value:expr,)*
}
) => {
{
type X = $struct;
X {
$($field: test!(#field $(#[$modifier])? $field: $value),)*
}
}
};
(
#field $field:ident: $value:expr
) => {
$value
};
(
#field #[nope] $field:ident: $value:expr
) => {
$value
};
}
But that doesn't help here because I only delegate $value, not $field: $value,. But I can't delegate the whole thing because syntactically that's not one token tree(?) that a macro could produce/not produce.
Is there a way, using this or another trick, to achieve this? Preferably avoiding procedural macros, if possible.

Simplifying a `match` using a Rust macro

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.")
};

Rust macro error: local ambiguity: multiple parsing options

The following rust code does not compile because of the macro error
error: local ambiguity: multiple parsing options: built-in NTs stmt ('s') or 1 other option.
macro A is fine. Macro B shows the error.
macro_rules! A {
($x: ident, $($s: stmt)*) => {
println!("hello");
};
}
macro_rules! B {
($x: ident, $($s: stmt)*; $e: expr) => {
println!("hello");
};
}
fn main() {
A![my_name, let x=5];
B![my_name, let x=5; 5];
}
This minimal reproducible example in B is exactly what I need. I want the macro to accept multiple let statements and terminate by some other expression.
What is the ambiguity that is being referred to?
Is there a way around it?
Of those tokens accepted after statement fragments I have tried several combinations yet none appear to make a difference. Neither does swapping the statement with a token tree.
Expressions are statements, so $($s: stmt)*; $e: expr is ambiguous because the compiler can't decide between using s or e when it encounters one.
Since you only expect bindings, you can easily expand them yourself:
macro_rules! A {
($x: ident, $($s: stmt)*) => {
println!("hello");
};
}
macro_rules! B {
($x: ident, $(let $p:pat = $v:expr)*; $e: expr) => {
$(let $p = $v);*
println!("hello: {}", $e);
};
}
fn main() {
A![my_name, let x=5];
B![my_name, let x=5; x+2];
}
Note that this doesn't support including types in the binding (let a: i32 = 42;) because pat can't be followed by :.

How to combine different algorithms in one executable

To learn Rust, I have started to implement some of the Project Euler problems. Now I want to take the next step and create a console based user interface, which has the ability for running all or only specific problems. Another requirement is that the user should be able to pass optional parameters only to a specific problem.
My current solution is to have a Trait ProjectEulerProblem that declares for example run(). With that I can do something like this:
fn main() {
let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
let problems: Vec<Box<problems::ProjectEulerProblem>> = vec![
box problems::Problem1,
box problems::Problem2
];
match args.flag_problem {
Some(x) => println!("Result of problem: {} is {}", x, problems[x-1].run()),
None => println!("No problem number given.")
}
}
My question is, is there a way to get rid of the explicit problems vector initialization, maybe by using macros? Alternative ideas for implementing my application like described above are also welcome.
You can use a macro with repetition to generate your list without having to type out the full path and name every time.
macro_rules! problem_vec(
($( $prob:tt ),*) => ({
&[
$(
&concat_idents!(Proble, $prob),
)*
]
});
);
const PROBLEMS: &'static[&'static ProjectEulerProblem] = problem_vec!(m1, m2);
Note, you cannot simply use indices, because the concat_idents macro requires an identifier and numbers are not identifiers. concat_idents is also only available on nightly. On stable you need to give the entire struct name:
macro_rules! problem_vec(
($( $prob:ident ),*) => ({
&[
$(
&problems::$prob,
)*
]
});
);
const PROBLEMS: &'static [&'static problems::ProjectEulerProblem] = problem_vec!(
Problem1, Problem2
);
PlayPen
My mashup crate lets you define a concise way to build the problems array as problems![1, 2]. This approach works with any Rust version >= 1.15.0.
#[macro_use]
extern crate mashup;
mod problems {
pub trait ProjectEulerProblem {
fn run(&self);
}
pub struct Problem1;
impl ProjectEulerProblem for Problem1 {
fn run(&self) {
println!("running Project Euler problem 1");
}
}
pub struct Problem2;
impl ProjectEulerProblem for Problem2 {
fn run(&self) {
println!("running Project Euler problem 2");
}
}
}
macro_rules! problems {
($($number:tt),*) => {{
// Use mashup to define a substitution macro `m!` that replaces every
// occurrence of the tokens `"Problem" $number` in its input with the
// concatenated identifier `Problem $number`.
mashup! {
$(
m["Problem" $number] = Problem $number;
)*
}
// Invoke the substitution macro to build a slice of problems. This
// expands to:
//
// &[
// &problems::Problem1 as &problems::ProjectEulerProblem,
// &problems::Problem2 as &problems::ProjectEulerProblem,
// ]
m! {
&[
$(
&problems::"Problem" $number as &problems::ProjectEulerProblem,
)*
]
}
}}
}
fn main() {
for p in problems![1, 2] {
p.run();
}
}

Resources