Using attributes to make a template take arguments
Let's suppose we want to make our Constructor
template
a little more flexible:
we'd like to be able to give the new
function a different name.
The usual way to pass an option to a derive macro is with an attribute, something like this:
#[derive(Deftly)]
#[derive_deftly(Constructor)]
#[deftly(constructor(newfn="function_name"))]
struct Example(...);
We can extend our template to take a function name, like this:
#![allow(unused)] fn main() { use derive_deftly::define_derive_deftly; define_derive_deftly! { Constructor for struct: impl<$tgens> $ttype where $twheres { $tvis fn ${tmeta(constructor(newfn)) as ident} // (1) ( $( $fname: $ftype , ) ) -> Self { Self { $( $fname , ) } } } } use derive_deftly::Deftly; #[derive(Deftly)] #[derive_deftly(Constructor)] #[deftly(constructor(newfn="construct_example"))] struct Example { a: f64, b: String } }
Here, instead of specifying "new
"
for the method name in our template,
we give the name as ${tmeta(constructor(newfn))} as ident
.
This tells the template to look for an
#[deftly(constructor(newfn="..."))]
attribute on the type,
to interpret that attribute as an identifier,
and to use the value of that attribute
in place of the keyword.
The as ident
portion is mandatory;
without it, derive-deftly can't tell
how to parse the argument to the argument.
Instead of ident
, you can describe other kinds syntactical types,
such as str
, ty
, path
, or expr
.
See the reference for a complete list.
Note that we explicitly
named our attribute as constructor(newfn)
.
We could instead have specified it as newfn
,
but that would have the potential to conflict
with attributes interpreted by other templates.
As a best practice, if
we expect our template to be widely used,
we should namespace our attributes as in the example above.
The
$tmeta
("toplevel meta")
keyword that we used here
tells the template
to look at the #[deftly]
attributes for the type.
We can, instead, use
$vmeta
("variant meta")
to look for #[deftly]
attributes for the current variant,
or
$fmeta
("field meta")
to
to look for #[deftly]
attributes for the current field.
(We'll see an example of using $fmeta
in a little while.)