Why does the following macro expect a semi-colon when called? - rust

I'm trying to write a macro to switch between rayon's par_iter and std's iter depending on a build feature (possibly getting ahead of myself, as I've not read a lot on macros yet). A macro seems a bit better to deal with than a function here, since a function might need some relatively complex types to make this work; as well a macro might remain more flexible in the future if I wanted to add even more variations in build features pertaining to how to run iterators.
#[macro_export]
macro_rules! par_iter {
($($tokens:tt)*) => {
#[cfg(feature = "threaded")]
$($tokens)*.par_iter()
#[cfg(not(feature = "threaded"))]
$($tokens)*.iter()
}
}
I see the following error:
error: macro expansion ignores token `b_slice` and any following
--> src/util.rs:28:8
|
28 | $($tokens)*.iter();
| ^^^^^^^^^
|
::: src/counting.rs:219:9
|
219 | par_iter!(b_slice).map(WordCount::from)
| ------------------- help: you might be missing a semicolon here: `;`
| |
| caused by the macro expansion here
|
= note: the usage of `par_iter!` is likely invalid in expression context
While I have no idea about the first error, I'm curious why a ; is expected - how do I make it valid in expression context?

This essentially boils down to, that you aren't allowed to have attributes within expressions like that, e.g. that the following is invalid:
b_slice.iter()
#[cfg(not(feature = "threaded"))]
.map(|x| x)
.collect();
To fix the issue, you can assign them to a temporary variable, like this:
Note the double {{ and }}, which results in a block, such that the final expression is the value that block results in.
#[macro_export]
macro_rules! par_iter {
($($tokens:tt)*) => {{
#[cfg(feature = "threaded")]
let it = $($tokens)*.par_iter();
#[cfg(not(feature = "threaded"))]
let it = $($tokens)*.iter();
it
}};
}
Alternatively, you can also split it into two macros like this:
#[cfg(feature = "threaded")]
#[macro_export]
macro_rules! par_iter {
($($tokens:tt)*) => {
$($tokens)*.par_iter()
}
}
#[cfg(not(feature = "threaded"))]
#[macro_export]
macro_rules! par_iter {
($($tokens:tt)*) => {
$($tokens)*.iter()
}
}

Related

Rust macro to format variable number of arguments

In writing Rust code, I frequently want to create my error type in an error block:
match something() {
Ok(x) => {...},
Err(e) => Err(MyError::new(format!("Something failed: {}", e)))
}
There is a lot of repeated code in the Err creation that I would like to extract to a function/macro. Part of it is easy:
fn err(msg: String) -> Result<_, MyError> {
Err(MyError::new(msg))
}
However, there is still boilerplate in the error block:
Err(e) => err(format!("Something failed: {}", e))
Since I also want to be able to do things like
Err(e) => err(format!("Unable to convert {}: {}", input, e)
I can't write a single function to get rid of the format!. So clearly I need a macro. I currently have
#[macro_export]
macro_rules! foo {
($base: expr, $(arg:expr),*) => {{
Err(MyError::new(format!(base, arg)))
}};
}
I don't get errors with the macro definition, but trying to use it gives me compilation errors:
|
231 | macro_rules! foo {
| ---------------- when calling this macro
...
245 | let xxx = foo!("a", "b");
| ^^^ no rules expected this token in macro call
The examples that I find online deal with processing the arguments one at a time, such as bar![1,2,3] to push each value onto a vector. I can't figure out what I need to do to get the macro to match and then pass all the arguments to another macro.
You may be interested in the anyhow crate which contains an anyhow! macro doing something similar.
Your macro is almost good, you're just missing an extra $ in the declaration, and $ prefixes at the usage site.
#[macro_export]
macro_rules! foo {
($base: expr, $($args:tt),*) => {{
Err(MyError::new(format!($base, $($args),*)))
}};
}
Note that you can also pass the whole thing to format! without having a separate base parameter.
#[macro_export]
macro_rules! foo {
($($args:tt),*) => {{
Err(MyError::new(format!($($args),*)))
}};
}
If you wanted to prefix the error message with another string, format_args! can be used instead of format! to save a memory allocation.
#[macro_export]
macro_rules! foo {
($($args:tt),*) => {{
Err(MyError::new(format!("Something failed: {}", format_args!($($args),*))))
}};
}

How to match a closure with argument in a Rust macro [duplicate]

I'm trying to achieve something like this (simplified):
macro_rules! atest {
($closure:tt) => {
let x = 5;
println!("Result is {}", $closure())
};
}
fn main() {
//let x = 50;
atest!((|| 5 + x));
}
It does not work because the argument to the atest macro is considered by the compiler before macro evaluation:
error[E0425]: cannot find value `x` in this scope
--> src/main.rs:10:20
|
10 | atest!((|| 5 + x));
| ^ not found in this scope
Is it possible to make this work? My understanding was that macros are expanded before compilation.
Peter's answer explains why what you're doing won't work. This is part of what's referred to as "macro hygiene": things declared inside macros can't "leak" into the surrounding scope.
A common workaround for the problem you're facing is to pass the name of the identifier into the macro as another argument:
macro_rules! atest {
($x:ident, $closure:tt) => {
let $x = 5;
println!("Result is {}", $closure())
};
}
fn main() {
atest!(x, (|| 5 + x));
}
This will work because naming x puts it in the caller's scope, even though the declaration is inside the macro.
You might notice that the closure is kind of unnecessary, at least in this example -- you could just pass 5 + x as an expression to the macro and have it expanded inline.
macro_rules! atest {
($x:ident, $value:expr) => {
let $x = 5;
println!("Result is {}", $value)
};
}
You call this macro like atest!(x, 5 + x), which looks a little bit like a closure of its own. That might give you the idea of writing atest!(|x| 5 + x) instead. And that will also work, with a variable scoped to the closure:
macro_rules! atest {
($closure:expr) => {
let x = 5;
println!("Result is {}", $closure(x))
};
}
References
Macros in Rust pt1
Is it possible to make this work? My understanding was that macros are expanded before compilation?
Macros are expanded before compilation, but not before parsing. The raw input code has already been parsed and macros operate on an abstract syntax tree, not on text. For example, the closure is already understood to be a closure, and its free variables are already bound to variables in its lexical scope.
This is different to some other languages macros, for example C/C++, which operate on raw text, and let you screw things up pretty badly if you're not careful.

Different versions of the same function with conditional compilation in Rust

I'm trying to create two different versions of the same function, only one of which will be compiled. As an example:
#[cfg(debug_assertions)]
fn do_something(x: usize) -> usize {
x + 1
}
#[cfg(not(debug_assertions))]
fn do_something() -> usize {
0
}
This works fine and I can also call the correct version of do_something if I know which one I'm calling (In practice, the functions will do the exact same thing, the debug one just requires more info for some validation). So I can create two corresponding main functions:
#[cfg(debug_assertions)]
fn main() {
do_something(0);
}
#[cfg(not(debug_assertions))]
fn main() {
do_something();
}
But this is very clumsy, and I want to have only one version of the code that doesn't depend on debug_assertions. I'd like to do something like:
macro_rules! call {
($func:ident, $optional_arg:expr) => {{
if cfg!(debug_assertions) {
$func($optional_arg);
} else {
$func();
}
}};
}
fn main() {
call!(do_something, 0);
}
and then re-use the macro wherever I call this function, or similar ones. But this doesn't compile:
--> main.rs:16:13
|
2 | fn do_something(x: usize) -> usize {
| ---------------------------------- defined here
...
16 | $func();
| ^^^^^-- supplied 0 arguments
| |
| expected 1 argument
...
22 | call!(do_something, 0);
| ----------------------- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
I don't understand why I get the error, since the wrong function call shouldn't even be compiled.
The error can be fixed by forcing the functions to have the same signature and simply ignoring the unnecessary argument in the release version, but that doesn't seem like the right way to go.
What would you do in this situation? Why doesn't the macro example compile?
From the reference :
cfg!, unlike #[cfg], does not remove any code and only evaluates
to true or false. For example, all blocks in an if/else expression
need to be valid when cfg! is used for the condition, regardless of
what cfg! is evaluating
Flags will be evaluated in compile time but you are doing this check at runtime. You need to use attributes to avoid the problem:
macro_rules! call {
($func:ident, $optional_arg:expr) => {{
#[cfg(debug_assertions)]
$func($optional_arg);
#[cfg(not(debug_assertions))]
$func();
}};
}

Macro expansion ignores token `let` and any following

I want to define a variable with a macro ident and pass it to a derive macro, but I get an error:
Error
error: macro expansion ignores token `let` and any following
--> database/src/models.rs:15:9
|
15 | let struct_name = stringify!($name);
| ^^^
...
50 | / model!(Person {
51 | | name: String
52 | | });
| |___- caused by the macro expansion here
|
= note: the usage of `model!` is likely invalid in item context
Code
#[macro_export]
macro_rules! model {
(
$(#[$met: meta])*
$name: ident { $($attr: ident : $typ: ty),* }
) => {
let struct_name = stringify!($name);
//let table_name = struct_name.to_camel_case();
dbg!("{}", struct_name);
#[derive(Debug)]
struct $name {
name: String
};
};
}
model!(Person {
name: String
});
Playground
You use the macro model! where the compiler expects an item (function/struct/trait declaration or impl block). A let statement is only valid inside a code block. This is why your first example works (where the macro call is inside main), while your second example doesn't.
This seems like an instance of the XY problem. Check out procedural macros if you want to run code to generate code at compile time.

Creating environment for closure in a macro in Rust

I'm trying to achieve something like this (simplified):
macro_rules! atest {
($closure:tt) => {
let x = 5;
println!("Result is {}", $closure())
};
}
fn main() {
//let x = 50;
atest!((|| 5 + x));
}
It does not work because the argument to the atest macro is considered by the compiler before macro evaluation:
error[E0425]: cannot find value `x` in this scope
--> src/main.rs:10:20
|
10 | atest!((|| 5 + x));
| ^ not found in this scope
Is it possible to make this work? My understanding was that macros are expanded before compilation.
Peter's answer explains why what you're doing won't work. This is part of what's referred to as "macro hygiene": things declared inside macros can't "leak" into the surrounding scope.
A common workaround for the problem you're facing is to pass the name of the identifier into the macro as another argument:
macro_rules! atest {
($x:ident, $closure:tt) => {
let $x = 5;
println!("Result is {}", $closure())
};
}
fn main() {
atest!(x, (|| 5 + x));
}
This will work because naming x puts it in the caller's scope, even though the declaration is inside the macro.
You might notice that the closure is kind of unnecessary, at least in this example -- you could just pass 5 + x as an expression to the macro and have it expanded inline.
macro_rules! atest {
($x:ident, $value:expr) => {
let $x = 5;
println!("Result is {}", $value)
};
}
You call this macro like atest!(x, 5 + x), which looks a little bit like a closure of its own. That might give you the idea of writing atest!(|x| 5 + x) instead. And that will also work, with a variable scoped to the closure:
macro_rules! atest {
($closure:expr) => {
let x = 5;
println!("Result is {}", $closure(x))
};
}
References
Macros in Rust pt1
Is it possible to make this work? My understanding was that macros are expanded before compilation?
Macros are expanded before compilation, but not before parsing. The raw input code has already been parsed and macros operate on an abstract syntax tree, not on text. For example, the closure is already understood to be a closure, and its free variables are already bound to variables in its lexical scope.
This is different to some other languages macros, for example C/C++, which operate on raw text, and let you screw things up pretty badly if you're not careful.

Resources