Errors when i try to compile this macro definition


#1

This code doesn’t compile, what am i doing wrong?

struct NiceStruct {
    inner: u64
}

struct LovelyStruct {
    inner: NiceStruct
}

macro_rules! new {
	($inner:item) => (
	    LovelyStruct{ inner }
	)
}

fn main() {
    let nice = NiceStruct { inner: 5 };
    let lovely = new!(nice);
}

It produces this error: Which is not very helpful imo. :confused:

   Compiling playground v0.0.1 (file:///playground)
error: expected one of `!` or `::`, found `<eof>`
  --> src/main.rs:21:23
   |
21 |     let lovely = new!(nice);
   |                       ^^^^ expected one of `!` or `::` here

error: Could not compile `playground`.

To learn more, run the command again with --verbose.

playground


#2

(Disclaimer: I’m not a macro expert)

I don’t think item is the right type here. In this case, nice is an identifier so the following works:

struct NiceStruct {
    inner: u64,
}

struct LovelyStruct {
    inner: NiceStruct,
}

macro_rules! new {
	($inner:ident) => (
	    LovelyStruct{ inner: $inner }
	)
}

fn main() {
    let nice = NiceStruct { inner: 5 };
    let lovely = new!(nice);
}

Don’t forget to use $ when referring to metavariables within the macro body (i.e. $inner, not inner).


#3

yeah thanks this works


#4

Probably want :expr instead.

macro_rules! new {
	($inner:expr) => (
	    LovelyStruct{ inner: $inner }
	)
}

General rules of thumb:

(note: in these examples, $pat means you should match that sort of thing with $something:pat, and the same goes for $path, $ident, $expr, etc.)

let $pat = $expr;

for $pat in $expr $block

match $expr {
    $pat => $expr,
    $pat if $expr => $expr,
}

fn $ident($pat: $ty) $block   // <-- best choice where possible
fn $ident($ident: $ty) $block // <-- sometimes you're forced to do this

let $ident = |$pat| $expr;

// function call
$expr($expr)  // <-- best choice where possible, works even for closures
$path($expr)  // <-- second best, still supports e.g. `Option::Some::<u64>`
$ident($expr) // <-- please don't do this

// struct member access
$expr.$ident

// struct method (this won't support the turbofish syntax, but I don't
//                think anything will)
$expr.$ident($expr)

// struct declaration
struct $ident { $ident: $ty }

// struct destructuring
let $path { $ident: $pat } = $expr;

mod $ident {
    $($item)*
}