Oleh Olexandr
Oleh Olexandr

Reputation: 43

rust struct fields generating by macro

I want to create a macro which on the received model generates structures with all its fields and implements trait Default, but faced such problem. My macro struct_field! is recognized as a field, not a macro inside the model!. Мaybe someone knows how to solve this problem

macro_rules! struct_field {
    ($fild_name:ident: string = $def_val:expr,) => {
        pub $fild_name: Arc<RwLock<String>>
    };
    
    ($fild_name:ident: string = $def_val:expr, $($fields:tt)+) => {
        pub $fild_name: Arc<RwLock<String>>
        struct_field!($($fields)+)
    };

    ($fild_name:ident: bool = $def_val:expr,) => {
        pub enable_kernel_settings: Arc<AtomicBool>,
    };

    ($fild_name:ident: bool = $def_val:expr, $($fields:tt)+) => {
        pub $fild_name: Arc<AtomicBool>,
        struct_field!($($fields)+)
    };
}

macro_rules! model {
    ($model:ident { $($inner_model:ident : $inner_model_name:ident { $($fields:tt)+ },)+ }) => {
        pub struct $model {$(
                $inner_model: $inner_model_name,
        )+}

        $(pub struct $inner_model_name {
            struct_field!($($fields)+) // <-- error
        })+
    };
}

error:

error: expected `:`, found `!`
--> .../mod.rs:43:29
   |
43 |                   struct_field!($($fields)+)
   |                               ^ expected `:`
...
48 | / model! {
49 | |     MainStruct {
50 | |         inner_struct1: InnerStruct1 {
51 | |             feild1: bool = true,
...  |
58 | |     }
59 | | }
   | |_- in this macro invocation

example:

model! {
    MainStruct {
        inner_struct1: InnerStruct1 {
            feild1: bool = true,
        },
        inner_struct2: InnerStruct2 {
            feild1: bool = false,
            feild2: bool = false,
            feild3: string = "ignore",
        },
    }
}

expected result:

pub struct MainStruct {
    inner_struct1: InnerStruct1,
    inner_struct2: InnerStruct2,
}

pub struct InnerStruct1 {
    feild1: Arc<AtomicBool>,
}

pub struct InnerStruct2 {
    feild1: Arc<AtomicBool>,
    feild2: Arc<AtomicBool>,
    feild3: Arc<RwLock<String>>
}

you can test this with "cargo expand" and you should be enabled rust-nightly

Upvotes: 4

Views: 1677

Answers (1)

jthulhu
jthulhu

Reputation: 8657

Contrary to C-like macros, Rust macros can only operate on valid AST nodes, that is, they can only be passed something that can be tokenized and, if needed, parsed to some extent, and can only "return" a valid AST node. This, in particular, rules out the invocation of a macro that would be extended as a field definition, because something like a: bool is not a valid AST node.

It is still possible to make a macro to do what you want to do, using function-style procedural macros, but it may become somehow more convoluted.

Upvotes: 2

Related Questions