Is it possible in rust to write a macro that creates other macros. For example, suppose I define the following two macros:
macro_rules! myprint(
($a:expr) => (print!("{}", $a))
)
macro_rules! myprintln(
($a:expr) => (println!("{}", $a))
)
Since the two macros repeat a lot of code, I may want to write a macro to generate the macros.
I've tried to generate such a meta macro.
#![feature(macro_rules)]
macro_rules! metamacro(
($i:ident) => (
macro_rules! $i (
($a:expr) => ({println!("hello {}", $a)})
)
);
)
metamacro!(foo)
fn main() {
foo!(1i);
}
but get the following errors:
<anon>:6:13: 6:14 error: unknown macro variable `a`
<anon>:6 ($a:expr) => ({println!("hello {}", $a)})
^
playpen: application terminated with error code 101
Program ended.
Edit: After playing around with the macros some more, I discovered that a higher order macro works as expected if the returned macro doesn't receive any arguments. For example, the following code
#![feature(macro_rules)]
macro_rules! metamacro(
($i:ident) => (
macro_rules! $i (
() => ({println!("hello")})
)
);
)
metamacro!(foo)
fn main() {
foo!();
}
prints hello
This was recently made possible in #34925. The answer below refers to older versions of Rust.
This is #6795 and #6994, and isn't directly possible at the moment. E.g. the first thing one would try is
#![feature(macro_rules)]
macro_rules! define {
($name: ident, $other: ident) => {
macro_rules! $name {
($e: expr) => {
$other!("{}", $e)
}
}
}
}
define!{myprintln, println}
define!{myprint, print}
fn main() {}
But this fails with
so8.rs:6:13: 6:14 error: unknown macro variable `e`
so8.rs:6 ($e: expr) => {
^
(Filed #15640 about the caret pointing at the ( instead of the $e.)
The reason is macro expansion is "dumb": it naively interprets and replaces all the tokens, even inside nested macro invocations (and macro_rules! is a macro invocation). Hence, it can't tell that the $e inside the interior macro_rules! are actually meant to be left as $e: it's just trying to replace them when expanding define.
The second thing one might try is passing in extra arguments to define to place in the interior definition, so that the $e doesn't appear directly in the nested invocation:
#![feature(macro_rules)]
macro_rules! define {
($name: ident, $other: ident, $($interior: tt)*) => {
macro_rules! $name {
($($interior)*: expr) => {
$other!("{}", $($interior)*)
}
}
}
}
define!{myprintln, println, $e}
define!{myprint, print, $e}
fn main() {}
That is, I added the $interior: tt to allow capturing the $e. tt stands for token tree, which is a non-delimiter raw token (e.g. $ or some_ident) or a sequence of tokens surrounded by matching delimiters (e.g. (foo + bar fn "baz")). Unfortunately, it seems expansion is eager enough for the $e expansion to be expanded too:
so8.rs:8:13: 8:14 error: unknown macro variable `e`
so8.rs:8 ($($interior)*: expr) => {
^
It works fine when the nested macro has no argument itself, because there is no nested $... non-terminals and so the expansion phase doesn't ever hit the definition problem described above.
Lastly, you can get some semblance of code sharing since you can expand to macro invocations by name:
#![feature(macro_rules)]
macro_rules! my {
($name: ident, $e: expr) => {
$name!("{}", $e)
}
}
fn main() {
my!(print, 1i);
my!(println, 2i);
}
which prints 12.
Here is one example that works, perhaps a starting point for you:
#![feature(macro_rules)]
macro_rules! metamacro(
($i:ident) => (
macro_rules! $i (
() => ({println!("hello!")})
)
);
)
metamacro!(hello)
fn main() {
hello!();
}
Related
I have macro calls another macro. This is a simplified version of the file (the actual outer macro has matching logic).
macro_rules! outer_macro {
() => (
inner_macro!()
)
}
macro_rules! inner_macro{
() => (
// stuff
)
}
Everything worked when I had these macros in the same file where outer_macro was called (src/caller.rs), but I moved them to their own file src/macros.rs and now src/caller.rs can't find outer_macro.
I tried adding #[macro_export] above outer_macro and pub mod macros; to src/lib.rs. Now src/caller.rs can find outer_macro, but outer_macro can't find inner_macro.
#[macro_export]
macro_rules! outer_macro {
() => (
inner_macro!()
^^^^^^^^^^^
)
}
macro_rules! inner_macro{
() => (
// stuff
)
}
How do I make outer_macro usable to the rest of my crate while keeping inner_macro in its scope?
The problem here is that the macro doesn't actually reference inner_macro; it instead simply copy-pastes the string inner_macro!() into wherever the macro is getting used. Therefore, the inner_macro has to be a valid path everywhere; with other words, use an absolute path inside of outer_macro:
src/macros.rs:
macro_rules! outer_macro {
() => {
crate::macros::inner_macro!()
};
}
macro_rules! inner_macro {
() => {
// stuff
};
}
pub(crate) use inner_macro;
pub(crate) use outer_macro;
src/main.rs:
mod macros;
fn main() {
macros::outer_macro!();
}
"How do I make outer_macro usable to the rest of my crate while keeping inner_macro in its scope?"
You can't, that's the point of macros: they acutally replace the macro invocation with new code, that means that the new code that is getting inserted has to be valid for the invocation location as well. Meaning, if you use an inner_macro call inside of outer_macro, both have to be visible at wherever outer_macro is being used.
An alternative would be to use internal rules, as #Jmb pointed out:
macro_rules! outer_macro {
() => {
crate::macros::outer_macro!(#inner_macro)
};
(#inner_macro) => {
// stuff
}
}
pub(crate) use outer_macro;
This would in fact hide the inner macro.
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),*))))
}};
}
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.
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),+
}
This works, but test_macro accepts only one argument:
macro_rules! test_define (
($name:ident) => (
macro_rules! $name (
( $x:expr ) => (
// something
)
);
)
);
test_define!(test_macro);
If I try and do this:
macro_rules! test_define2 (
($name:ident) => (
macro_rules! $name (
( $($x:expr),* ) => (
// something
)
);
)
);
test_define2!(test_macro2);
Compilation fails with:
error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
--> src/main.rs:4:16
|
4 | ( $($x:expr),* ) => (
| ^^^^^^^^^
It is a known bug that nested macros don't allow repetitions in binding patterns (issue #35853).
Unfortunately, there is no workaround. The only solution is to change your API to not rely on repetition inside nested macros.
While it is not possible to do this directly, as kennytm described in this answer, you could do this using procedural macros(in case you really need to, otherwise I would not recommend it).
While it's probably a lot simpler when using nightly, it is also possible in stable.
Limitations
Please tell me in case I missed anything or in case there might be problems when using custom derive like this in the future.
You need to create a struct, which causes one of the following problems
there is either a maximum of 1 macro call per module
the user is required to add another string of characters when calling the outer macro
you create a private module which contains the required struct and could be accessed
So let's just copy the example from here, and just look at this part of the code:
fn impl_hello_world(ast: &syn::DeriveInput) -> quote::Tokens {
let name = &ast.ident;
quote! {
impl HelloWorld for #name {
fn hello_world() {
println!("Hello, World! My name is {}", stringify!(#name));
}
}
}
}
inside of the quote! macro, you are not limited to the implementation of the struct. you could change this to
quote! {
macro_rules! #name {
($($expr:expr),*) => {
// something
}
}
}
now you have a macro with the same name as a struct which takes an infinite amount of arguments.
To do this in another macro, the outer macro just has to look like this:
macro_rules! test_define {
($name:ident) => {
#[allow(non_camel_case_types)] // because macro names are lower case
#[allow(dead_code)] // because this struct should never be used
#[derive(HelloWorld)]
struct $name { }
}
};
And now you can call test_define and then the inner macro:
test_define!(foo);
fn main() {
foo!()
}
However, there is still one problem: people could accidentally access your struct. So there are ways to circumvent this(each solution is a directly linked the problem with the same number):
name the struct in a way which prevents accidental access by pure chance:
macro_rules! test_define {
($name:ident) => {
#[allow(dead_code)]
#[derive(HelloWorld)]
struct Dgfggsdfgujksdsdsdfsdfsdg {
$name: u8,
}
}
};
You have to change your proc macro to use the struct field instead of name inside of quote! and in case the test_define! is called more than once in the same crate you have 2 structs with identical names which causes a compile time error.
to prevent two identical struct names you could also change test_define! to take an additional argument:
macro_rules! test_define {
($name:ident, $rep_guard:ident) => {
#[allow(non_camel_case_types)]
#[allow(dead_code]
#[derive(HelloWorld)]
struct $rep_guard {
$name: u8,
}
}
};
You are using a struct field instead of the name with this method. To use you now have to write test_define!(foo,fvgfdgdfgdfgd), which is really awkward, so I would not recommend this.
This is probably the best option, now you can keep your strange struct name from solution 1 and just put the whole thing in a module. Meaning that no one can accidentally access the created struct and you can have an infinite amount of calls to test_define!.
macro_rules! test_define {
($name:ident) => {
#[macro_use] // to use the macro in the current module
mod $name {
#[allow(dead_code)]
#[derive(HelloWorld)]
struct Dgfggsdfgujksdsdsdfsdfsdg {
$name: u8,
}
}
}
};
The compiler should simply remove all of those structs, as they are dead_code
(at least when building with the --release flag). You could adapt the quote! by adding #[macro_export] if you need it.
Another advantage is that proc macros use your source code in the same way as a String or as Tokens which can be cast to a String, this means that you could create multiple macros, for example:
test_derive!(foo) => foo!(), foo_with_var!(75)
In case there is anything you don't understand, just ask.
You can define arbitrary meta macros if you get the parent macro to write the outer $ signs. A way to do it is to pass a $ in your parent macro and store it in a variable so you can use it later.
For you can define test_define2 as follow:
macro_rules! test_define2 (
(($dollar:tt) $name:ident) => (
macro_rules! $name (
( $dollar($x:expr),* ) => (
// something
)
);
);
);
Calling it like this:
test_define2(($) test_macro2);
will expand to:
macro_rules! test_macro2 (
( $($x:expr),* ) => (
($($x),*) // or something
)
);
Which works as expected.
To avoid having to add the extra ($) when calling meta macros you can write a second macro that call the meta macro with the added symbol or if it is possible, add this final match arm:
macro_rules! test_define2 (
(($d:tt) $name:ident) => (
macro_rules! $name (
( $d($x:expr),* ) => (
($d($x),*) // or something
)
);
);
($($t:tt)+) => (
// parenthesis or curly braces are necessary to properly "escape" the $
test_define2!(($) $($t)+);
);
);
// test_define2!(test_macro2);
// expands to:
// test_define2!(($)test_macro2);
// expands to:
// macro_rules!test_macro2(
// ($($x:expr),*) => (
// ($($x),*) // or something
// )
// );