I have created a function-like procedural macro. I call it with a declarative macro as parameter. I was hoping to find a way that the declarative macro is expanded before processing the procedural macro (or expand it inside the procedural macro). Example (code is also available at https://github.com/Jasperav/rec_macro):
Call site:
macro_rules! t {
($e:expr) => {
$e.to_string() + " I am a macro"
}
}
fn main() {
re!(t!("hi"));
}
Proc macro crate:
#[proc_macro_hack]
pub fn re(input: TokenStream) -> TokenStream {
panic!("{:#?}", input) // I can see "hi" and something like t!, but not the result of the macro expansion
}
Inside the panic message, I see see that the declarative macro (t) is inside. I just want the raw value of hi I am a macro in this case (or some syn type). In my case, the declarative macro should always expand to a &str or String.
Is it possible to expand t before invoking re, or to expand t manually inside the proc macro? I want to see the result of the macro (in this case: "hi I am a macro") inside my proc functional like macro.
Thanks.
Related
I would like to generate a variable type and initializer from a (procedural) macro. Since macros consume and generate streams (and not trees) of tokens, morally, it should be possible to do something like:
extern crate quote;
use quote::quote;
#[proc_macro]
pub fn foo(tokens: TokenStream) -> TokenStream {
TokenStream::from(quote! {
: u8 = 42
})
}
pub static FOO foo!();
But alas, this fails with
|
28 | pub static FOO foo!();
| ^^^ expected one of `:`, `;`, or `=`
Now, of course I could turn this inside out and require writing something like
foo!(FOO)
which would expand to
pub static FOO: u8 = 42;
but the reason I would like to do things piecewise is to be able to use the same macro to generate types-and-initializers in different contexts (e.g. for local let bindings, not just statics).
You can't do what you are trying to do, not because : u8 = 42 is not correctly converted to a TokenStream, but simply because macro invocations (independently of their definition) cannot occur anywhere. In particular, they can only occur where a handle of the TokenStream would be reduced to a node, which is not the case in your example. This is because Rust has to parse the file once, before it knows anything about what macros do, exactly to find out all macros. In this first step, it only knows about its own syntax. It is smart enough to say "ok, this whole node of my AST is just this macro invocation" without knowing what the macro invocation will actually do, but it can't say "this macro invocation will feed me these tokens, so the next token I should read is ;" (which would be the case in your example), because it doesn't know what the macro does yet!
To convince yourself, image that the macro foo!() produces the token mod A {, and bar!() the token }. Then, the following example auto-destroys itself
foo!()
#[proc_macro]
fn foo(_: TokenStream) -> TokenStream { ... }
#[proc_macro]
fn bar(_: TokenStream) -> TokenStream { ... }
bar!()
Because if you expand correctly the macros, then they hide themselves by putting themselves into a module, which prevents the macros from being expanded, which makes them visible again, ... Even worse examples (where the issue occurs before visibility checks, at the parsing step) are easy to provide as soon as you allow what you are trying to do.
Let us say I have a function like follows:
fn log(msg: &str) {
//fancy_output
println!("{}", msg)
}
Now, if I want to log a variable using the function, I must do it like so:
let x = 5;
log(&format!("{:?}", x)); // Assume some complex data type which implements Debug
Clearly this is a lot of boilerplate. I could remove the & by making the argument a string, but that does not remove my bigger problem: using format!() everywhere.
How can I write a function/macro such that I can do the following or similar:
let x = 5;
log("{:?}", x) // Assume some complex data type which implements Debug
I know a place to start would be looking at the format! source code, but it is quite hard to understand for a beginner like me and how I might implement it here.
Do I use some fancy macro or is there a simpler way?
format! is a macro, not a function, which is why it is able to work with variable number of arguments. You can do the same with a declarative macro like this:
macro_rules! log {
($($args: tt)*) => {
println!($($args)*);
}
}
The $($args: tt)* means that the macro accepts zero or more (*) of any kind of token (tt). Then it just passes these on to the println macro.
Which you can call like this:
fn main() {
let x = 5;
log!("{:?}", x);
}
I'm stuck trying to "rename" a identifier inside a declarative macro, I have the renaming function already, but can't find a way to "execute" it inside the macro. Is this possible with declarative macros?
Example:
fn to_pascal_case(s: &str) -> String { ... }
macro_rules! test_gl_enum {
($n: ident) => { struct to_pascal_case(stringify!($n)); }
}
test_gl_enum!(VERTEX_SHADER);
expands to:
struct to_pascal_case(());
desired result would be:
struct VertexShader;
You can't execute arbitrary code in a declarative macro. You have a couple of options:
Proc Macro
Proc macros are basically just functions that take a TokenStream and return a TokenStream (with some variations between function-like, attribute and derive macros). They can contain arbitrary code, so you could absolutely parse the identifier and apply some transformation to it.
If you're going to take this route, you will likely want to use syn and quote, and I'd also very strongly recommend going through the proc macro workshop, which is a series of exercises to teach you the basics.
Or use a crate
As is often the case, someone else has also had this problem, and has written a crate for it: https://crates.io/crates/casey
With that, you can write:
use casey::*;
macro_rules! test_gl_enum {
($n:ident) => {
struct pascal!($n);
};
}
Here, pascal!() is a proc macro that does the transformation, and since it's a macro (rather than a function) it is also evaluated at compile time, so the expansion won't look like struct pascal!(my_enum_name);
I have the type Foo:
pub struct Foo { ... }
Now I want to create a procedural macro that creates an instance of this struct. This might involve heavy computation, file access, or other stuff only procedural macros can do, but the exact details of how to create that instance are not important here.
I defined my procedural macro like this:
#[proc_macro]
pub fn create_foo(_: TokenStream) -> TokenStream {
let foo_value: Foo = /* some complex computation */;
// TODO: return `foo_value`
}
The users of my procedural macros should be able to write this:
fn main() {
let a: Foo = create_foo!();
}
Please note that Foo could contain a lot of data, like many megabytes of Vec data.
How can I return the Foo value from my procedural macro?
While this seems like an easy request, there is actually a lot to unroll here.
Most importantly, it is crucial to understand that procedural macros only return tokens (i.e. Rust code). To put it bluntly: the Rust compiler executes your procedural macro, takes the resulting tokens and pastes them in the users code where your procedural macro invocation was. You can think of procedural macros as a pre-processing step that takes your Rust code, transforms it and spits out another .rs file. That file is then fed to the compiler.
In order to "return a value of Foo" you have to return a TokenStream that represents an expression which evaluates to Foo. For example:
#[proc_macro]
pub fn create_foo(_: TokenStream) -> TokenStream {
quote! { Foo { data: vec![1, 2, 3] } }
}
In the user's crate:
let a: Foo = create_foo!();
Which would expand to:
let a: Foo = Foo { data: vec![1, 2, 3] };
The data: vec![1, 2, 3] part could be generated dynamically by the procedural macro. If your Foo instance is very large, the code creating that instance is probably very large as well. This means that compile times might increase because the Rust compiler has to parse and check this huge expression.
So you can't return the value directly? No. You might think that you could do it with unsafe code. For example, emit a big const DATA: &[u8] = ...; and mem::transmute it to Foo, but you can't for several reasons:
The procedural macro and the user's crate might not run on the same platform (CPU, OS, ...) which all might influence how Foo is represented in memory. The same Foo instance might be represented differently in memory for your procedural macro and your user crate, so you can't transmute.
If Foo contains heap allocated structures (Vec), you can't do it anyway.
If you must generate the value in your procedural macro, then there is only one solution to get it to the user, but this is not optimal. Alternatively, maybe calculating it at runtime once isn't that bad.
This extremely simple Rust program:
fn main() {
let c = "hello";
println!(c);
}
throws the following compile-time error:
error: expected a literal
--> src/main.rs:3:14
|
3 | println!(c);
| ^
In previous versions of Rust, the error said:
error: format argument must be a string literal.
println!(c);
^
Replacing the program with:
fn main() {
println!("Hello");
}
Works fine.
The meaning of this error isn't clear to me and a Google search hasn't really shed light on it. Why does passing c to the println! macro cause a compile time error? This seems like quite unusual behaviour.
This should work:
fn main() {
let c = "hello";
println!("{}", c);
}
The string "{}" is a template where {} will be replaced by the next argument passed to println!.
TL;DR If you don't care why and just want to fix it, see the sibling answer.
The reason that
fn main() {
let c = "hello";
println!(c);
}
Cannot work is because the println! macro looks at the string at compile time and verifies that the arguments and argument specifiers match in amount and type (this is a very good thing!). At this point in time, during macro evaluation, it's not possible to tell that c came from a literal or a function or what have you.
Here's an example of what the macro expands out to:
let c = "hello";
match (&c,) {
(__arg0,) => {
#[inline]
#[allow(dead_code)]
static __STATIC_FMTSTR: &'static [&'static str] = &[""];
::std::io::stdio::println_args(&::std::fmt::Arguments::new(
__STATIC_FMTSTR,
&[::std::fmt::argument(::std::fmt::Show::fmt, __arg0)]
))
}
};
I don't think that it's actually impossible for the compiler to figure this out, but it would probably take a lot of work with potentially little gain. Macros operate on portions of the AST and the AST only has type information. To work in this case, the AST would have to include the source of the identifier and enough information to determine it's acceptable to be used as a format string. In addition, it might interact poorly with type inference - you'd want to know the type before it's been picked yet!
The error message asks for a "string literal". What does the word "literal" mean? asks about what that means, which links to the Wikipedia entry:
a literal is a notation for representing a fixed value in source code
"foo" is a string literal, 8 is a numeric literal. let s = "foo" is a statement that assigns the value of a string literal to an identifier (variable). println!(s) is a statement that provides an identifier to the macro.
If you really want to define the first argument of println! in one place, I found a way to do it. You can use a macro:
macro_rules! hello {() => ("hello")};
println!(hello!());
Doesn't look too useful here, but I wanted to use the same formatting in a few places, and in this case the method was very helpful:
macro_rules! cell_format {() => ("{:<10}")}; // Pads with spaces on right
// to fill up 10 characters
println!(cell_format!(), "Foo");
println!(cell_format!(), 456);
The macro saved me from having to duplicate the formatting option in my code.
You could also, obviously, make the macro more fancy and take arguments if necessary to print different things with different arguments.
If your format string will be reused only a moderate number of times, and only some variable data will be changed, then a small function may be a better option than a macro:
fn pr(x: &str) {
println!("Some stuff that will always repeat, something variable: {}", x);
}
pr("I am the variable data");
Outputs
Some stuff that will always repeat, something variable: I am the variable data