Rust recursive macro not working for generating struct - rust

I am trying to write a macro that generates a struct in Rust. This macro will add different Serde attributes to struct fields based on the type of field. This is the final goal.
For now, I'm simply trying to write a macro that uses another macro for generating a recursive code.
This is what the code looks like:
macro_rules! f_list {
($fname: ident, $ftype: ty) => {
pub $fname: $ftype,
}
}
macro_rules! mk_str {
($sname: ident; $($fname: ident: $ftype: ty,)+) => {
#[derive(Debug, Clone)]
pub struct $sname {
$(
f_list!($fname, $ftype)
)+
}
}
}
mk_str! {
Yo;
name: String,
}
fn main() {
println!("{:?}", Yo { name: "yo".to_string() })
}
This code on running gives below error, which I'm not able to understand.
error: expected `:`, found `!`
--> src/main.rs:12:23
|
12 | f_list!($fname, $ftype);
| ^ expected `:`
What's wrong here?
Here's a playground link

When writing declarative macros (macro_rules!), it's important to understand that the output of a macro must be a pattern, a statement, an expression, an item or an impl. Effectively, you should think of the output as something that can stand alone, syntactically speaking.
In your code, the macro f_list, if it worked, would output code like
name1: type1,
name2: type2,
name3: type3,
While this could be part of a declaration for a struct, it is not itself something that can stand alone.
Why is the error in the other macro then? mk_str successfully expands to
#[derive(Debug, Clone)]
pub struct Yo {
f_list!(name, String)
}
However, the parser doesn't expect a macro inside of a struct declaration. The inside of a struct isn't a pattern, a statement, an expression, an item or an impl. Thus, when it sees the !, it gives up and reports an error.
How can you fix this? In this particular example, f_list is fairly redundant. You could simply replace f_list!($fname, $ftype) with pub $fname: $ftype, in mk_str and it'll work as written. If this doesn't work for your goal, take a look at The Little Book of Rust Macros. It has some patterns for doing very complicated things with macros. Most of the information in this answer came from the section "Macros in the AST".

Related

A macro registering marked structures into an enum

I am searching for a macro, or a procedural macro, which could register some marked structures (or enums) as variants of an enum.
Moreover, I would like, that if the marked structures are implementing a trait, then the trait would be implemented by this enum.
For example, I would like something like that:
trait MyTrait {
fn print_it(&self);
}
#[register(RegEnum,MyTrait)]
struct Foo;
#[register(RegEnum,MyTrait)]
struct Bar;
impl MyTrait for Foo {
fn print_it(&self) { println!("Foo"); }
}
impl MyTrait for Bar {
fn print_it(&self) { println!("Bar"); }
}
fn main() {
let reg_1 = RegEnum::Foo;
let reg_2 = RegEnum::Bar;
// This will print "Foo"
reg_1.print_it();
// This will print "Bar"
reg_2.print_it();
}
Notice that the macro should build the enum, by considering which structures are marked. The enum would not be defined by the programmer.
Is there a way to do that?
First, you have no way of knowing how much structs are annotated with the attribute. So you have to pass this number for each invocation.
The macro should cache the struct, and when that number is reached, it'll build and emit the trait. It should also error if the number is already reached, so you'll get future-proof against new structs instead of silent miscompilation.
Take a look in enum_dispatch's source for an example of a macro that does that.
Note that order of items is not guaranteed, and moreover, the macro can be called with only part of the items with incremental compilation. Also, as far as I am aware, there is no guarantee that the compiler will use the same process for each invocation, so theoretically it may break, but practically it works well (not sure whether it still works with incremental compilation, though).

Lint violation not caught when using macro_rules imported from another crate

I'm implementing a crate that has the purpose of wrapping some user code and make sure it is called in a certain context. I can summarize my workspace this way:
A "user_code" crate:
This contains a single "user" function, which I would have no control over. Notice that it returns a u32.
pub fn addition(a: u32, b: u32) -> u32 {
a + b
}
A "wrapper" crate:
This contains a macro_rules. This macro creates some modules and a caller function, which will call the user code seen above. This is a highly simplified version. Notice that the return value of addition is ignored.
#[macro_export]
macro_rules! macro_wrapper {
() => {
pub mod my_user_code {
use wrapper::*;
use user_code::*;
pub mod addition {
use wrapper::*;
use super::*;
pub fn caller(a: u32, b: u32) {
addition(a, b);
}
}
}
};
}
An "example" crate:
Here I simply call the wrapper macro, in order to form the modules and functions I need and then use in the main function.
#![deny(unused_results)]
use wrapper::macro_wrapper;
macro_wrapper! {
}
fn main() {
my_user_code::addition::caller(2,3);
println!("Hello, world!");
}
This might seem like super strange code (it is) but it's a very simplified example of a legit project :)
My goal now is to make sure that this code doesn't compile because the return value of addition is not used - it was forgotten. The lint #![deny(unused_results)] seems to achieve exactly what I want.
However, this works only if, instead of use wrapper::macro_wrapper; I have the macro directly defined in the main.rs. In that case I get:
error: unused result of type `u32`
--> example\src\main.rs:14:21
|
14 | addition(a, b);
| ^^^^^^^^^^^^^^^
...
21 | / macro_wrapper! {
22 | |
23 | | }
| |_- in this macro invocation
|
note: the lint level is defined here
--> example\src\main.rs:1:9
|
1 | #![deny(unused_results)]
| ^^^^^^^^^^^^^^
= note: this error originates in the macro `macro_wrapper` (in Nightly builds, run with -Z macro-backtrace for more info)
If the macro is defined in its crate "wrapper", the code compiles and runs just fine without any error. This is not the result I expect.
What am I missing here? Thank you in advance for reading!
The unused_results lint doesn't get fired for macro expansions for external macros. This is by design, because this lint can be very noisy, and you can't control the expansion of external macros.
If you need that, your best bet is to mark your function as #[must_use], then #![deny(unused_must_use)]. Because the unused_must_use lint does get fired for external macros, it will work.
If you can't control the function definition, a neat trick you can use is to define a dummy function whom only job is to trigger the lint:
#[doc(hidden)]
#[must_use]
pub fn must_use<T>(v: T) -> T { v }
// In the macro...
pub fn caller(a: u32, b: u32) {
$crate::must_use(addition(a, b));
}

How to define different function names with a macro?

I've been tyring to define a different function for eacu unsigned type in Rust, and of course they have to have different names because Rust can't pick the right one by signature. So I'm trying to give them different names but I was unable to do so:
macro_rules! define_my_function {
($t:ty, $f_name:ident) => {
pub fn concat_idents!(my_function_, $ty)()(
stream: &mut T
) -> Result<$ty, ParseError> {
todo!()
}
}
}
define_my_function!(u8, name_for_u8);
Compiling playground v0.0.1 (/playground)
error: expected one of `(` or `<`, found `!`
--> src/lib.rs:3:29
|
3 | pub fn concat_idents!(my_function_, $ty)()(
| ^ expected one of `(` or `<`
...
11 | define_my_function!(u8, name_for_u8);
| ------------------------------------- in this macro invocation
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9ccea3ddcc75bb2cfab14c52df7bc24f
It looks like this is impossible in Rust. What can I do then? Also I don't want to use concat_idents as it's nightly only
You can use the paste crate, that internally uses a procedural macro to create new identifiers:
macro_rules! define_my_function {
($t:ty) => {
::paste::paste! {
pub fn [<my_function_ $t>]() {
println!(stringify!($t));
}
}
}
}
define_my_function!(u8);
define_my_function!(u16);
fn main() {
my_function_u8();
my_function_u16();
}
Playground
Note that I had to slightly change the code you gave because it wasn't compiling.
From the documentation for std::concat_idents:
Also, as a general rule, macros are only allowed in item, statement or expression position. That means while you may use this macro for referring to existing variables, functions or modules etc, you cannot define a new one with it.
Unfortunately, it seems that at the time this is possible using a procedural macro, or by manually copy-pasting for each numeric type.

Store state in macro_rules

I'd like to create a macro that operates on a given list of types, but I need to be able to store which other types are being processed.
A simple example of something I want to do:
struct Foo;
struct Bar {
foo: Foo,
data: u32,
}
baz!(Foo, Bar);
// outputs
struct OptFoo;
struct OptBar {
foo: OptFoo,
data: u32
}
The problem is that it doesn't seem like macro_rules allows me to store a temporary state (i.e a HashSet where I would tag which types are part of the macro invocation). The only workaround I have in mind is to write what I want as a proc_macro_derive and manually adding a custom attribute for each type I need but that's obviously far from perfect...
Edit:
The question is similar to this one, but here I'mm trying to save a state locally and temporarily (basically doing two passes over the arguments while storing data about those) in a single macro call. However it seems it's also not possible.
As pointed by #trentcl, what I want to achieve is indeed possible with proc macro (I thought proc macros were limited to Derive and attributes...)
#[proc_macro]
pub fn generate(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = proc_macro2::TokenStream::from(input);
println!("{:?}", input);
proc_macro::TokenStream::from(input)
}
generate!(struct Foo;);
// outputs its argument without changing anything, i.e:
// struct Foo ;
The previous example demonstrates a trivial macro that prints to sdout the parsed input: TokenStream [Ident { ident: "struct", span: #0 bytes(330..336) }, Ident { ident: "Foo", span: #0 bytes(337..340) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(340..341) }]
Note that it parses the tokens but does not create an AST; we'll have to use syn for that.
This repo has examples many of what can be done with proc macros, pretty helpful!

How to introspect all available methods and members of a Rust type?

Is there a way to print out a complete list of available members of a type or instance in Rust?
For example:
In Python, I can use print(dir(object))
In C, Clang has a Python API that can parse C code and introspect it.
Being unfamiliar with Rust tools, I'm interested to know if there is some way to do this, either at run-time or compile-time, either with compiler features (macros for example), or using external tools.
This question is intentionally broad because the exact method isn't important. It is common in any language to want to find all of a variable's methods/functions. Not knowing Rust well, I'm not limiting the question to specific methods for discovery.
The reason I don't define the exact method is that I assume IDEs will need this information, so there will need to be some kinds of introspection available to support this (eventually). For all I know, Rust has something similar.
I don't think this is a duplicate of Get fields of a struct type in a macro since this answer could include use of external tools (not necessarily macros).
Is there a way to print out a complete list of available members of a type or instance in Rust?
Currently, there is no such built-in API that you can get the fields at runtime. However you can retrieve fields by using two different ways.
Declarative Macros
Procedural Macros
Solution By Using Declarative Macro
macro_rules! generate_struct {
($name:ident {$($field_name:ident : $field_type:ty),+}) => {
struct $name { $($field_name: $field_type),+ }
impl $name {
fn introspect() {
$(
let field_name = stringify!($field_name);
let field_type = stringify!($field_type);
println!("Field Name: {:?} , Field Type: {:?}",field_name,field_type);
)*
}
}
};
}
generate_struct! { MyStruct { num: i32, s: String } }
fn main() {
MyStruct::introspect();
}
This will give you the output:
Field Name: "num" , Field Type: "i32"
Field Name: "s" , Field Type: "String"
Playground
Solution Using Procedural Macro
Since procedural macros are more complicated from the declarative macros, you better to read some references(ref1, ref2, ref3) before starting.
We are going to write a custom derive which is named "Instrospect". To create this custom derive, we need to parse our struct as a TokenStream with the help of syn crate.
#[proc_macro_derive(Introspect)]
pub fn derive_introspect(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemStruct);
// ...
}
Since our input can be parsed as ItemStruct and ItemStruct has the fields() method in it, we can use this to get fields of our struct.
After we get these fields, we can parse them as named and we can print their field name and field type accordingly.
input
.fields
.iter()
.for_each(|field| match field.parse_named() {
Ok(field) => println!("{:?}", field),
Err(_) => println!("Field can not be parsed successfully"),
});
If you want to attach this behavior to your custom derive you can use the following with the help of the quote crate:
let name = &input.ident;
let output = quote! {
impl #name {
pub fn introspect(){
input
.fields
.iter()
.for_each(|field| match field.parse_named() {
Ok(field) => println!("{:?}", field),
Err(_) => println!("Field can not be parsed successfully"),
});
}
}
};
// Return output TokenStream so your custom derive behavior will be attached.
TokenStream::from(output)
Since the behaviour injected to your struct as introspect function, you can call it in your application like following:
#[derive(Introspect)]
struct MyStruct {
num: i32,
text: String
}
MyStruct::introspect();
Note: Since the example you are looking for similar to this question. This Proc Macro Answer and Declarative Macro Answer should give you insight as well
To expand on my comment, you can use rustdoc, the Rust documentation generator, to view almost everything you're asking for (at compile time). rustdoc will show:
Structs (including public members and their impl blocks)
Enums
Traits
Functions
Any documentation comments written by the crate author with /// or //!.
rustdoc also automatically links to the source of each file in the [src] link.
Here is an example of the output of rustdoc.
Standard Library
The standard library API reference is available here and is available for anything in the std namespace.
Crates
You can get documentation for any crate available on crates.io on docs.rs. This automatically generates documentation for each crate every time it is released on crates.io.
Your Project
You can generate documentation for your project with Cargo, like so:
cargo doc
This will also automatically generate documentation for your dependencies (but not the standard library).
I have written a very simple crate which uses procedural macro. It gives you access to members information plus some simple information about struct/enum you use. Information about methods can not be given because procedural macros simply can't get this information, and as far as I know, there are no any methods which may give such information.
I don't think there is anything that will do this out of the box.
It may be possible to write a compiler plugin which can do that by examining the AST.
If you need the field names inside your program then you probably need to use macros. Either wrap your struct definition in macro and pattern match to create some function to get their names, or use procedural macro to derive structs for traits with such functions.
See examples in syn for derived traits. In particular, see syn::Data::Struct which has fields.
According to question of #AlexandreMahdhaoui, I would say: at least on latest Rust versions the proc_macro from accepted answer will not work, because you will need to pass tokens into quote! using "#". So you could try smth like next:
use proc_macro::{TokenStream};
use quote::{quote, ToTokens};
use syn::{parse_macro_input, ItemStruct};
#[proc_macro_derive(Introspect)]
pub fn derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemStruct);
let ident = input.ident;
let field_data = input.fields.iter().map(|f| {
let field_type = f.ty.clone();
format!(
"Name={}, Type={}",
f.ident.clone().unwrap().to_string(),
quote!(#field_type).to_string()
)
}).collect::<Vec<_>>();
let output = quote! {
impl #ident {
pub fn introspect() {
println!("{:#?}", vec![#(#field_data),*]);
}
}
};
TokenStream::from(output)
}
#[derive(Introspect)]
struct Test {
size: u8,
test_field: u8,
}
fn main() {
Test::introspect();
}
Regarding methods, defined in impl I didn't find any info in output, so not sure if it possible. Probably somebody could share in comments ?
I use something like this:
println!("{:?}", variable); // struct, enum whatever
If it's a large type, use the # version:
println!("{:#?}", variable); // struct, enum whatever

Resources