Learning Macros 1.1

So I've decided to sit down and learn Macros 1.1, and documented my experience while doing so. I created custom derive that would create an iterator spanning all the variants of the enum (assuming that each variant implemented default of course). Perhaps someone else might find this useful.

Edit: Oh yeah, here is the link

7 Likes

Nicely done and thanks for the writeup! Real docs for this feature are coming, see @steveklabnik's comment in the tracking issue rust-lang/rust#35900.

The canonical way to report errors from a procedural macro is by panicking. There is an example of this in the syn readme.

One pro tip for quote! - you don't need a vec in order to repeat things. You can write a repetition over anything that is an IntoIterator (which includes all Iterators). So instead of this:

let types: Vec<_> = fields.iter().map(|f| &f.ty).collect();
quote! {
    #idx => #name::#id( #(#types::default(),)* ),
}

You can do:

let types = fields.iter().map(|f| &f.ty);
quote! {
    #idx => #name::#id( #(#types::default(),)* ),
}

In this case an even better option is this:

use std::iter;
let default = quote! {
    ::std::default::Default::default()
};
let elems = iter::repeat(&default).take(fields.len());
quote! {
    #idx => #name::#id( #(#elems),* ),
}

Here instead of generating something like Test::C(u32::default(), u32::default()) like your version, it will generate Test::C(Default::default(), Default::default()). The advantage is that this works for generic types like if the type of a field is Vec<u32>. The original version would have generated Vec<u32>::default() which is not valid Rust.

3 Likes

Thanks! I appreciate the feedback. I added these notes to the blog. I have a question, but I posted it reddit.