How to use proc_macro in normal module

Viewed 858

I have some code from hex_literal crate and it look like this:

#[proc_macro]
pub fn hex(input: TokenStream) -> TokenStream {
    let ts = TokenStream::from_iter(TokenTreeIter::new(input));
    TokenStream::from(TokenTree::Group(Group::new(Delimiter::Bracket, ts)))
}

This code won't compile with error:

error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type

Question: how to re-write this macro in normal module?

2 Answers

Question: how to re-write this macro in normal module?

It sounds a bit like you're asking how you can express it as a macro in a "normal" module. Which I interpret as using macro_rules, given that the hex proc macro is simply adding [...] square brackets around the input.

You can easily do that with macro_rules! like this:

macro_rules! hex {
    ($($tt:tt)*) => {
        [$($tt)*]
    };
}

Otherwise, as @MaxV already answered you need to create a separate crate and specify proc-macro = true.

Just to give a short an straightforward example. First, let's consider the following Cargo.toml:

[package]
name = "proc"
version = "0.1.0"
authors = []
edition = "2018"

[lib]
name = "hex"
path = "src/hex.rs"
proc-macro = true

[[bin]]
name = "bin"
path = "src/main.rs"

[dependencies]
syn = "1.0"

Then you create src/hex.rs, with the code you provided (just including the missing use declarations):

use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
use std::iter::FromIterator;

#[proc_macro]
pub fn hex(input: TokenStream) -> TokenStream {
    let ts = TokenStream::from_iter(input.into_iter());
    TokenStream::from(TokenTree::Group(Group::new(Delimiter::Bracket, ts)))
}

Then to use it, you can create src/main.rs:

fn main() {
    let x = hex::hex!(1 + 2, 3 + 4, 5 + 6);
}

Which expands to:

fn main() {
    let x = [1 + 2, 3 + 4, 5 + 6];
}

You have to create separated crate with

[lib]
proc-macro = true

You can't put proc-macro implementation into regular crate.

more details

Related