Creating environment for closure in a macro in Rust - 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.

Related

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.

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

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()
}
}

How to write a macro that displays the file and line number and a variable number of arguments?

I've found several useful macros in Rust, namely: file!(), line!(), stringify!() I've also found that Rust allows macros with variable arguments, as stated here:
macro_rules! print_all {
($($args:expr),*) => {{
$(
println!("{}", $args);
)*
}}
}
My goal is to somehow combine all those macros inside one which I will use during troubleshooting/debugging. So calling trace! macro on the following example:
let a: i32 = 1;
let b: i32 = 2;
trace!(a,b)
should expand to something like this:
println!("TRACE: file: {}, line: {}, a: {}, b: {}", file!(), line!(), a, b);
Is it possible? If yes, how would such a macro work?
You could do something like this:
macro_rules! trace {
($($args: expr),*) => {
print!("TRACE: file: {}, line: {}", file!(), line!());
$(
print!(", {}: {}", stringify!($args), $args);
)*
println!(""); // to get a new line at the end
}
}
There is likely a small overhead to calling print! multiple times, since each call will result in a system call and will also check for IO errors. However, constructing a single formatting string for arbitrary arguments would need a procedural macro, which I think is beyond the scope of the question.
You could also use a BufWriter to limit it to a single system call, but it might not be worth the effort.

Can I detect if an expression is a mutable variable using Rust's macro repetition?

macro_rules! log {
($($x:expr),*) => {
{
$(
//how to detect $x in the Macro repetition?
)*
}
};
}
I don't want to use ($($x:ident),*). The log! macro can log the expression and then I want to match the variable type.
Macros only can access token streams, and cannot say anything more about those tokens, apart from what is explicitly given to the macro.
For example, consider this code:
fn main() {
let mut foo: i32 = 1;
my_macro!(foo);
}
The only information available to the macro my_macro is the input token foo. It can match that foo is is an identifier but it can't say anything more about it. The fact that there is mutable binding called foo, or that this binding is an i32 are just not available inside the macro.

Identifier interpolation in macro definition [duplicate]

Basically, there are two parts to this question:
Can you pass an unknown identifier to a macro in Rust?
Can you combine strings to generate new variable names in a Rust macro?
For example, something like:
macro_rules! expand(
($x:ident) => (
let mut x_$x = 0;
)
)
Calling expand!(hi) obvious fails because hi is an unknown identifier; but can you somehow do this?
ie. The equivalent in C of something like:
#include <stdio.h>
#define FN(Name, base) \
int x1_##Name = 0 + base; \
int x2_##Name = 2 + base; \
int x3_##Name = 4 + base; \
int x4_##Name = 8 + base; \
int x5_##Name = 16 + base;
int main() {
FN(hello, 10)
printf("%d %d %d %d %d\n", x1_hello, x2_hello, x3_hello, x4_hello, x5_hello);
return 0;
}
Why you say, what a terrible idea. Why would you ever want to do that?
I'm glad you asked!
Consider this rust block:
{
let marker = 0;
let borrowed = borrow_with_block_lifetime(data, &marker);
unsafe {
perform_ffi_call(borrowed);
}
}
You now have a borrowed value with an explicitly bounded lifetime (marker) that isn't using a structure lifetime, but that we can guarantee exists for the entire scope of the ffi call; at the same time we don't run into obscure errors where a * is de-referenced unsafely inside an unsafe block and so the compiler doesn't catch it as an error, despite the error being made inside a safe block.
(see also Why are all my pointers pointing to the same place with to_c_str() in rust?)
The use a macro that can declare temporary variables for this purpose would considerably ease the troubles I have fighting with the compiler. That's why I want to do this.
Yes however this is only available as a nightly-only experimental API which may be removed.
You can pass arbitrary identifier into a macro and yes, you can concatenate identifiers into a new identifier using concat_idents!() macro:
#![feature(concat_idents)]
macro_rules! test {
($x:ident) => ({
let z = concat_idents!(hello_, $x);
z();
})
}
fn hello_world() { }
fn main() {
test!(world);
}
However, as far as I know, because concat_idents!() itself is a macro, you can't use this concatenated identifier everywhere you could use plain identifier, only in certain places like in example above, and this, in my opinion, is a HUGE drawback. Just yesterday I tried to write a macro which could remove a lot of boilerplate in my code, but eventually I was not able to do it because macros do not support arbitrary placement of concatenated identifiers.
BTW, if I understand your idea correctly, you don't really need concatenating identifiers to obtain unique names. Rust macros, contrary to the C ones, are hygienic. This means that all names of local variables introduced inside a macro won't leak to the scope where this macro is called. For example, you could assume that this code would work:
macro_rules! test {
($body:expr) => ({ let x = 10; $body })
}
fn main() {
let y = test!(x + 10);
println!("{}", y);
}
That is, we create a variable x and put an expression after its declaration. It is then natural to think that x in test!(x + 10) refers to that variable declared by the macro, and everything should be fine, but in fact this code won't compile:
main3.rs:8:19: 8:20 error: unresolved name `x`.
main3.rs:8 let y = test!(x + 10);
^
main3.rs:3:1: 5:2 note: in expansion of test!
main3.rs:8:13: 8:27 note: expansion site
error: aborting due to previous error
So if all you need is uniqueness of locals, then you can safely do nothing and use any names you want, they will be unique automatically. This is explained in macro tutorial, though I find the example there somewhat confusing.
There is also https://github.com/dtolnay/paste, which works well in cases where concat_idents is underpowered or in cases where you can't target the nightly compiler.
macro_rules! foo_macro {
( $( $name:ident ),+ ) => {
paste! {
#[test]
fn [<test_ $name>]() {
assert! false
}
}
};
}
In cases where concat_idents doesn't work (which is most cases I'd like to use it) changing the problem from concatenated identifiers to using namespaces does work.
That is, instead of the non-working code:
macro_rules! test {
($x:ident) => ({
struct concat_idents!(hello_, $x) {}
enum contact_idents!(hello_, $x) {}
})
}
The user can name the namespace, and then have preset names as shown below:
macro_rules! test {
($x:ident) => ({
mod $x {
struct HelloStruct {}
enum HelloEnum {}
}
})
}
Now you have a name based on the macro's argument. This technique is only helpful in specific cases.
You can collect your identifiers into a struct if you don't want to use nightly and external crates and your identifiers are types.
use std::fmt::Debug;
fn print_f<T: Debug>(v: &T){
println!("{:?}", v);
}
macro_rules! print_all {
($($name:ident),+) => {
struct Values{
$($name: $name),+
}
let values = Values{
$(
$name: $name::default()
),+
};
$(
print_f(&values.$name);
)+
};
}
fn main(){
print_all!(String, i32, usize);
}
This code prints
""
0
0
If you fear that Value will conflict with some type name, you can use some long UUID as part of the name:
struct Values_110cf51d7a694c808e6fe79bf1485d5b{
$($name:$name),+
}

Resources