Background
In rust, it is very easy to init a struct:
struct Foo {
a: usize
}
let foo = Foo { a: 1 };
Problem
However, if the struct's name is given as a parameter in macros by example, it becomes strange:
mod a {
pub struct Foo {
pub a: usize
}
}
macro_rules! build_foo {
($Foo: ty) => {
$Foo { a: 1 }
};
}
fn main() {
let foo = build_foo!(a::Foo);
}
It turns out error:
expected expression, found `a::Foo`
If I change category of $Foo
to be ident
, it turns out error:
no rules expected the token `::`
since the struct we give is a path not ident.
If I change category of $Foo
to be path
, it turns out error:
macro expansion ignores token `{` and any following
I have tried ty
, ident
, path
, expr
, item
, tt
, ... None of them works.
Workarounds
I know there are two workarounds:
- make
$Foo
to beident
, and passFoo
directly withuse a::Foo
:
mod a {
pub struct Foo {
pub a: usize
}
}
macro_rules! build_foo {
($Foo: ident) => {
$Foo { a: 1 }
};
}
use a::Foo;
fn main() {
let foo = build_foo!(Foo);
}
- Write a constructor method for
Foo
, and pass$Foo
asty
:
mod a {
pub struct Foo {
pub a: usize
}
impl Foo {
pub fn new(a: usize) -> Self {
Self { a }
}
}
}
macro_rules! build_foo {
($Foo: ty) => {
<$Foo>::new(1)
};
}
fn main() {
let foo = build_foo!(a::Foo);
}
I think the two methods above are workarounds, but not solutions, and I wonder how to write the correct macros by example to achieve the goals.