Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a custom derive macro?

Tags:

I'm trying to write my own derive mode macro in Rust, and the documentation on it is somewhat lacking in examples.

I have a struct like:

#[derive(MyMacroHere)]
struct Example {
    id: i64,
    value: Option<String>,
}

I want my macro to generate a method à la

fn set_fields(&mut self, id: i64, value: Option<String>) {
    // ...
}

What are the basic steps to use the TokenStream trait to achieve something like that?

like image 551
Lev Avatar asked Nov 03 '18 22:11

Lev


People also ask

What is a derive macro?

Derive macros define new inputs for the derive attribute. These macros can create new items given the token stream of a struct, enum, or union. They can also define derive macro helper attributes.

What does #[ derive mean in Rust?

The derive attribute allows new items to be automatically generated for data structures. It uses the MetaListPaths syntax to specify a list of traits to implement or paths to derive macros to process.

Why are Rust macros useful?

Rust has excellent support for macros. Macros enable you to write code that writes other code, which is known as metaprogramming. Macros provide functionality similar to functions but without the runtime cost. There is some compile-time cost, however, since macros are expanded during compile time.

What are custom derive macros in rust?

This code example is from the syn examples repo, which is an excellent resource to learn about procedural macros. Custom derive macros in Rust allow auto implement traits. These macros enable you to implement traits using # [derive (Trait)]. syn has excellent support for derive macros.

How do I create a custom derive?

For example, syn's documentation starts with an example of a custom derive. You will parse the struct (or enum or union) and then handle the various ways of defining a struct (unit, tuple, named fields). You'll collect the information you need (type, maybe name), then you'll generate the appropriate code. See also:

How do I write an attribute-like macro?

To write an attribute-like macro, start by creating a project using cargo new macro-demo --lib. Once the project is ready, update the Cargo.toml to notify cargo the project will create procedural macros. Now we are all set to venture into procedural macros.

How do I create a procedural macro?

1 Answer 1 ActiveOldestVotes 25 Create a crate for your procedural macros: cargo new my_derive --lib Edit the Cargo.toml to make it a procedural macro crate: [lib] proc-macro = true Implement your procedural macro:


1 Answers

  1. Create a crate for your procedural macros:

     cargo new my_derive --lib
    
  2. Edit the Cargo.toml to make it a procedural macro crate:

     [lib]
     proc-macro = true
    
  3. Implement your procedural macro:

     extern crate proc_macro;
    
     use proc_macro::TokenStream;
    
     #[proc_macro_derive(MyMacroHere)]
     pub fn my_macro_here_derive(input: TokenStream) -> TokenStream { 
         // ...
     }
    
  4. Import the procedural macro and use it:

     extern crate my_derive;
    
     use my_derive::MyMacroHere;
    
     #[derive(MyMacroHere)]
     struct Example {
         id: i64,
         value: Option<String>,
     }
    

The hard part is in implementation of the macro. Most people use the syn and quote crates to parse the incoming Rust code and then generate new code.

For example, syn's documentation starts with an example of a custom derive. You will parse the struct (or enum or union) and then handle the various ways of defining a struct (unit, tuple, named fields). You'll collect the information you need (type, maybe name), then you'll generate the appropriate code.

See also:

  • How to Write a Custom derive Macro
  • Documentation for proc_macro
  • Documentation for syn
  • Documentation for quote
  • Is it possible to add your own derivable traits, or are these fixed by the compiler?
like image 101
Shepmaster Avatar answered Sep 25 '22 00:09

Shepmaster