Luke Dupin
Luke Dupin

Reputation: 2305

Rust macro strange error

I'm working through rust's macro system attempting to make an ORM wrapper over Diesel. Working through the belongs to, I found that rust is sending a rather unexpected error. Please consider the following code:

macro_rules! klass {
    (
        $name:ident,
        fields => ($(
            $field:ident : $type:ty = $default:expr,
        )*),
        belongs_to => ($(
            $belong_field:ident : $temp:ty [ $key:expr ],
        )*)
    ) => {
        struct $name {
            $( $field: $type, )*
        }
        impl Default for $name {
            fn default() -> $name {
                $name {
                    $(
                        $field : $default,
                    )*
                }
            }
        }
        impl $name {
            $(
                fn $belong_field(&self) -> $temp {
                    let key = $key;
                    $temp { ..Default::default() }
                }
            )*
        }
    }
}

klass! ( Neat,
        fields => (
            id: i32 = 0,
            name: String = String::from(""),
        ),
        belongs_to => ()
    );

klass!( Bob,
    fields => (
        id: i32 = 0,
        neat_id: i32 = 0,
    ),
    belongs_to => (
        neat: Neat [ "neat_id" ],
    )
);

fn main()
{
    let b = Bob { ..Default::default() };
    let mut n = b.neat();
}

This code will yield the following result:

error: expected expression, found `Neat`
  --> macro.rs:27:31
   |
27 |                       $temp { ..Default::default() }
   |   

Why does rust say it wants an expression instead of Neat? I can change this code to the following and everything in this example compiles and runs:

Neat { ..Default::default() }

Is this a compiler error given when "Neat" is inserted the program runs as expected? Thanks, apologizes for this mostly being code.

Upvotes: 2

Views: 915

Answers (1)

red75prime
red75prime

Reputation: 3861

$temp occurs in different contexts in macro expansion. As a type in fn $belong_field(&self) -> $temp and as a structure constructor in $temp { ..Default::default() }.

You've marked $temp as a type, it prevents second usage.

Replacing the line

$belong_field:ident : $temp:ty [ $key:expr ],

with

$belong_field:ident : $temp:ident [ $key:expr ],

allows the code to compile.

Upvotes: 4

Related Questions