# Introduction to derive-adhoc `derive-adhoc` allows you to write `#[derive]` macros -- macros driven by Rust data structures - by writing templates in a fairly straightforward template language. This is the friendly introduction. Of course there is a [reference manual](reference) too. * [Getting started with `derive_adhoc`.](#getting-started-with-derive_adhoc) * [Exporting templates](#exporting-templates) * [If you're only deriving once...](#if-youre-only-deriving-once) * [A brief tutorial: How to write your own Clone](#a-brief-tutorial-how-to-write-your-own-clone) * [Simple templates: fields and repetition](#simple-templates-fields-and-repetition) * [Making MyClone apply to generics](#making-myclone-apply-to-generics) * [Making MyClone apply conditionally](#making-myclone-apply-conditionally) * [Deriving for enumerations](#deriving-for-enumerations) * [Some more advanced topics](#some-more-advanced-topics) * [Transforming names and strings](#transforming-names-and-strings) * [Another example: Defining a constructor function.](#another-example-defining-a-constructor-function) * [Marking a template's limitations](#marking-a-templates-limitations) * [Debugging templates](#debugging-templates) * [Working with visibility](#working-with-visibility) * [Using attributes to make a template take arguments](#using-attributes-to-make-a-template-take-arguments) * [Getting started with conditionals](#getting-started-with-conditionals) * [More complicated conditionals](#more-complicated-conditionals) * [Other features](#other-features) ## Getting started with `derive_adhoc`. There are two parts to using `derive_adhoc`: specifying _templates_ that you can use to derive new features for your structs and enums and then _applying_ those templates to your types. To define a template, you use [`define_derive_adhoc!`](crate::define_derive_adhoc), as in ``` use derive_adhoc::define_derive_adhoc; define_derive_adhoc! { NamePrinter = impl $ttype { pub fn print_name() { println!("The name of this type is {}", stringify!($ttype)); } } } ``` This is a very simple template: it uses a single expansion: [`$ttype`](crate::doc_reference#ftype-vtype-ttype-tdeftype--types). (We'll get into more expansions, and more useful examples, later on. For now, all you need to know is that `$ttype` expands to the type on which you're applying your template.) Later on, you can apply `NamePrinter` to your own type, with [`#[derive(Adhoc)]`](crate::Adhoc), as in: ``` # use derive_adhoc::define_derive_adhoc; # define_derive_adhoc! { # NamePrinter = # # impl $ttype { # pub fn print_name() { # println!("The name of this type is {}", stringify!($ttype)); # } # } # } use derive_adhoc::Adhoc; #[derive(Clone, Debug, Adhoc)] #[derive_adhoc(NamePrinter)] pub struct MyStruct; MyStruct::print_name(); ``` ### Exporting templates But now suppose that you want to expose your template, so that people can use it from any crate. To do this, you use [`pub` before the name of your macro](define_derive_adhoc#exporting-a-template-for-use-by-other-crates). ``` pub trait NamePrinter { fn print_name(); } derive_adhoc::define_derive_adhoc! { /// Derives `NamePrinter`, providing `print_name` pub NamePrinter = impl $crate::NamePrinter for $ttype { fn print_name() { println!("The name of this type is {}", stringify!($ttype)); } } } ``` Note that this time, we've defined `NamePrinter` as a trait, and we've changed our template to refer to that trait as `$crate::NamePrinter`. The [`$crate`](crate::doc_reference#crate--root-of-template-crate) syntax will expand to the name of the crate in which our template was defined, so that when later we expand this template, it can find the right template. We've also added a [doc comment](define_derive_adhoc#docs-in-define), which will appear in the public API documentation for our crate. Additionally, [we need to re-export `derive_adhoc`](define_derive_adhoc#you-must-re-export-derive_adhoc-usually-no-semver-implications) from our crate, so that users get the correct version: ```rust,ignore // you might want to apply #[doc(hidden)] to this. pub use derive_adhoc; ``` Now, when somebody wants to use our template, they can do it like this: ```rust,ignore // Let's pretend our crate is called name_printer. use name_printer::{ // This is the trait we defined... NamePrinter, // This is the macro that makes our template work. // (We might come up with a better syntax for this later). derive_adhoc_template_NamePrinter. }; use derive_adhoc::Adhoc; #[derive(Adhoc)] #[derive_adhoc(NamePrinter)] struct TheirStructure { // ... } ``` ### If you're only deriving once... If you want, you can [apply a template to an existing type](derive_adhoc) without having to name that template. You might want to do this if you have a template that you only want to apply to a single struct, and so you don't want to bother naming it. Supposing that you wanted to apply the template above to `MyStruct` and `MyStruct` alone, you could have said: ``` use derive_adhoc::{Adhoc, derive_adhoc}; #[derive(Clone, Debug, Adhoc)] pub struct MyStruct; derive_adhoc!{ MyStruct: impl $ttype { pub fn print_name() { println!("The name of this type is {}", stringify!($ttype)); } } } ``` Of course, that's not so useful yet. In this case, it would have been easier just to write `impl MyStruct { pub fn print_name() { ... } }`. But soon, we'll see how to write more interesting templates, and how to use them to create much more interesting code. The rest of this document will focus on how to use derive_adhoc's template features to your fullest advantage. If you're the kind of person who wants to skip straight to the reference manual, you can find it [over here][reference]. ## A brief tutorial: How to write your own Clone For the next few sections, for a toy example, we'll be using `derive_adhoc` to define our own version of `derive(Clone)`. At first, it will be very simple; later on, we'll add a few features that Rust's `derive(Clone)` doesn't have. > Aside: > > We've picked a simple trait to derive on purpose, > so that we can focus on the features of `derive_adhoc` > without the additional complexity > of introducing an unfamiliar trait as well. > > Please let us know if this approach works for you! > We're learning how to explain these concepts > as we go along. ### Simple templates: fields and repetition Let's imagine we had to write Clone from scratch for a simple structure like this: ``` struct GiftBasket { n_apples: u8, n_oranges: u8, given_from: Option, given_to: String, } ``` We'd probably write something like this: ``` # struct GiftBasket { # n_apples: u8, # n_oranges: u8, # given_from: Option, # given_to: String, # } impl Clone for GiftBasket { fn clone(&self) -> Self { Self { n_apples: self.n_apples.clone(), n_oranges: self.n_oranges.clone(), given_from: self.given_from.clone(), given_to: self.given_to.clone() } } } ``` (In reality, since `n_apples` and `n_oranges` both implement `Copy`, you wouldn't actually call `clone()` on them. But since the compiler knows their types, it should be smart enough to optimize the Clone away for you.) If you imagine generalizing this to any simple struct struct with named fields, you might come up with a pseudocode template like this one: ```text,ignore impl Clone for ⟪Your struct⟫ { fn clone(&self) -> Self { Self { for each field: ⟪field name⟫: self.⟪field name⟫.clone(), } } } ``` And here's how that pseudocode translates into a `derive_adhoc` template: ``` use derive_adhoc::define_derive_adhoc; define_derive_adhoc! { MyClone = impl Clone for $ttype { fn clone(&self) -> Self { Self { $( $fname : self.$fname.clone() , ) } } } } ``` Let's look at that template. You've already seen `$ttype`: it expands to the type on which you are applying the macro. There are two new pieces of syntax here, though: [`$( ... )`](crate::doc_reference#repetition-and-nesting) and [`$fname`](crate::doc_reference#fname-vname-tname--names). In `derive_adhoc` templates, `$( ... )` denotes repetition: it repeats what is inside it an "appropriate" number of times. (We'll give a definition of "appropriate" later on.) Since we want to clone every field in our struct, we are repating the `field: self.field.clone() ,` part of our implementation. The `$fname` expansion means "the name of a field". Which field? Since `$fname` occurs inside `$( ... )`, we will repeat the body of the `$( ... )` once for each field, and expand `$fname` to the name of a different field each time. (Again, more advanced repetition is possible; there's [more to come](#a-further-note-on-reptition).) > ### On naming > > Many `derive_adhoc` expansions' > [names start with](crate::doc_reference#keyword-initial-letters) > `t` for **top-level** > (whatever you are applying the template to), > `v` for **variant** > (a variant of an `enum`), > or `f` for **field** > (a single field of a struct or variant). > > So far, you've seen `$ttype` for "top-level type" and `$fname` for > "field name". > > (We say "top-level" instead of "struct": > later on, we'll be showing you how to apply > derive_adhoc to `enums`.) > > Many `derive_adhoc` expansions end with > a short identifier for what they contain. > For example, `$tname` is the name of a top-level type, > `$vname` is the name of a variant, > and `$fname` is the name of a field. > Whenever possible, we have tried to use the same > identifier for the `t`, `v`, and `f` cases, > whenever it is logical. #### Will MyClone apply to other kinds of struct? Rust defines several kinds of struct: structs with fields (`struct Foo {...};`), tuple structs (`struct Foo(...);`), and unit structs (`struct Foo;`). If you try to apply the `MyClone` template above to a struct with fields, it will work just fine. But with a tuple struct, or a unit struct, you might expect it to fail. Surprisingly, it will still work fine! This isn't because of any clever trickery from `derive_adhoc`: it's just how Rust works. When you use it on tuple or unit structs, the `MyClone` template we wrote above will expand to code like this... which happens to be valid syntax! ``` struct TupleStruct(String, Option); impl Clone for TupleStruct { fn clone(&self) -> Self { Self { 0: self.0.clone(), 1: self.1.clone(), } } } struct UnitStruct; impl Clone for UnitStruct { fn clone(&self) -> Self { Self { } } } ``` This will be a common theme in what follows: Rust often lets you use a slightly unidiomatic syntax so that you can handle many different cases in the same way. ### Making MyClone apply to generics But here's a structure where our current `MyClone` implementation will fall flat: ``` # use std::fmt::Debug; struct MyItems where U: Clone + Debug { things: Vec, items: Vec } ``` When we go to expand the template, it will generate something like: ```rust,ignore impl Clone for MyItems { ... } ``` That isn't valid! We need to use the generic parameters, like so: ```rust,ignore impl Clone for MyItems where U: Clone+Debug { ... } ``` We can expand our `MyClone` definition to look that way: ``` # use derive_adhoc::define_derive_adhoc; define_derive_adhoc! { MyClone = impl<$tgens> Clone for $ttype where $twheres { fn clone(&self) -> Self { Self { $( $fname : self.$fname.clone() , ) } } } } ``` Here we meet two new expansions. [`$tgens`](crate::doc_reference#tgens-tgnames-twheres-tdefgens--generics) ("top-level generics") becomes the generic parameters as declared on the top-level type. (In our case, that's `$T:Clone, U`.) [`$twheres`](crate::doc_reference#tgens-tgnames-twheres-tdefgens--generics) ("top-level where clauses") becomes the `where` constraints as declared on the top-level type. (In our case, that's `U: Clone+Debug`.) Note that `$ttype` expands to the top-level _type_: that's now `MyItems`, which is what we want. If we had wanted only `MyItems`, we would say [`$tname`](crate::doc_reference#fname-vname-tname--names) instead. Will this template still work for non-parameterized types? Again, yes! To Rust, this syntax is perfectly fine: ```rust struct Simple { a: String } impl<> Clone for Simple where { fn clone(&self) -> Self { Self { a: self.a.clone(), } } } ``` ### Making MyClone apply conditionally Now, for the first time, we will make MyClone do something that Rust's `#[derive(Clone)]` does not: it will apply only when the fields of a struct are `Clone`. For example, suppose have a struct like this: ``` # use std::sync::Arc; struct Indirect(Arc, u16); ``` If you try to derive `Clone` on it, the compiler will generate code something like this: ```rust,ignore impl Clone for Indirect { ... } ``` But that `T: Clone` constraint isn't necessary: `Arc` always implements `Clone`, so your struct could be clone unconditionally. But using `derive_adhoc`, you can define a template that derives `Clone` only for the cases where the _actual_ required constraints are met: ``` # use derive_adhoc::define_derive_adhoc; define_derive_adhoc! { MyClone = impl<$tgens> Clone for $ttype where $twheres $( $ftype : Clone , ) { fn clone(&self) -> Self { Self { $( $fname : self.$fname.clone() , ) } } } } ``` Here, we are using [`$ftype`](crate::doc_reference#ftype-vtype-ttype-tdeftype--types), the type of a field. Since we're repeating it with `$( ... )`, we are requiring every field to be `Clone`. Will this work with non-generic fields, or if the same field is used more than once? Once again, yes! To Rust, this is a perfectly valid example: ```rust,ignore impl Clone for Direct where T: Clone, T: Clone, String: Clone { ... } ``` > This time, > `derive_adhoc` has exactly _one_ piece cleverness at work. > It makes sure that either `$twheres` is empty, > or that it ends with a comma. > That way, your template won't expand to something like > `where U: Debug + Clone T: Clone` > (which is a syntax eror) > or soemthing like > `where ,` > (which is also a syntax error). ### Deriving for enumerations At this point, you've probably noticed that we've defined `MyClone` to apply to `struct`s only, but it won't (yet) work on `enum`s. Let's fix that! Suppose that we have enumeration defined like this: ``` enum AllTypes { NoData, Tuple(u8, u16), Struct { a: String, b: String } } ``` We want to make sure that MyClone can recognize and re-construct each of the three variants. We can do that as follow (For simplicity, we're going to ignore generics for now.) ``` # use derive_adhoc::define_derive_adhoc; define_derive_adhoc! { MyClone = impl Clone for $ttype { fn clone(&self) -> Self { match self { $( $vpat => $vtype { $( $fname: $fpatname.clone(), ) }, ) } } } } ``` Note that now we have two levels of nested repetition. First, we match once for each variant. (This is at the `$vpat` and `$vtype` level.) Then we match once for each field of each variant. (This is at the `$fname` and `$fpatname` level.) Let's go over the new expansions here. First, we have [`$vpat`](crate::doc_reference#vpat-fpatname--pattern-matching-and-value-deconstruction): that expands to a pattern that can match and deconstruct a single variant. Then, we have [`$vtype`](crate::doc_reference#ftype-vtype-ttype-tdeftype--types): that's the type of the variant, suitable for use as a constructor. Then, inside the variant, we have `$fname`: that's our field name, which we've seen it before. Finally, we have [`$fpatname`](crate::doc_reference#vpat-fpatname--pattern-matching-and-value-deconstruction): that is the name of the variable that we used for this field in the pattern that deconstructed it. When we apply `MyClone` to our enumeration, we get something like this: ``` # enum AllTypes { NoData, Tuple(u8, u16), Struct { a: String, b: String } } impl Clone for AllTypes { fn clone(&self) -> Self { match self { AllTypes::NoData {} => AllTypes::NoData {}, AllTypes::Tuple { 0: f_0, 1: f_1, } => AllTypes::Tuple { 0: f_0.clone(), 1: f_1.clone() }, AllTypes::Struct { a: f_a, b: f_b, } => AllTypes::Struct { a: f_a.clone(), b: f_b.clone() }, } } } ``` Note that our template above will still work fine on a regular struct, even though it's written for an `enum`. If we apply `MyClone` above to `struct Example { a: u8, b: String }`, we get this: ``` # struct Example { a: u8, b: String } impl Clone for Example { fn clone(&self) -> Self { match self { Example { a: f_a, b: f_b, } => Example { a: f_a.clone(), b: f_b.clone(), } } } } ``` So (in this case at least) we were able to write a single template expansion that worked for both `struct`s and enum`s. #### Putting the generics back into our enumeration-friendly template Now let's see how it works when we try to handle generics again. (It's surprisingly straightforward!) ``` # use derive_adhoc::{Adhoc, define_derive_adhoc}; define_derive_adhoc! { MyClone = impl<$tgens> Clone for $ttype where $( $ftype: Clone, ) $twheres { fn clone(&self) -> Self { match self { $( $vpat => $vtype { $( $fname: $fpatname.clone(), ) }, ) } } } } # #[derive(Adhoc)] # #[derive_adhoc(MyClone)] # enum TestCase { Variant(A) } ``` ##### A further note on reptition Note that when we define our additional `where` clauses, we don't have to specify separate of repetition for variants and fields: if we just have `$ftype` in a top-level repetition, `derive_adhoc` will iterate over all fields in all variants. Sometimes, if you do something subtle, derive-adhoc may not be able to figure out what you're trying to repeat over. You can use [`${for ...}`](crate::doc_reference#repetition-and-nesting) to specifiy explicitly: so, above, instead, we could have written ``` # use derive_adhoc::{Adhoc, derive_adhoc}; # #[derive(Adhoc)] # enum TestCase { Variant(A) } # derive_adhoc! { TestCase: # fn testcase<$tgens>() where ${for fields { $ftype: Clone, }} # {} # } ``` You can also use `${for ...}` rather than `$(...)` in cases where you feel it makes your macro code clearer. ## Some more advanced topics Now that we've our first basic example under our belt, let's look at some other things that `derive_adhoc` can do. ### Transforming names and strings Often, it's useful to define new identifiers based on existing ones, or to convert identifiers into strings. You _could_ use the existing [`paste`] crate for this, or you can use a native facility provided by `derive_adhoc`. For example, suppose that you want to define a template that makes a "discriminant" type for your enumerations. You want the new type to be named `FooDiscriminant`, where `Foo` is the name of your existing type. While you're at it, you want to add an `is_` function to detect each variant. You can do that like this: ``` # use derive_adhoc::define_derive_adhoc; define_derive_adhoc! { Discriminant = #[derive(Copy,Clone,Eq,PartialEq,Debug)] enum ${paste $tname Discriminant} { $( $vname, ) } impl<$tgens> $ttype where $twheres { fn discriminant(&self) -> ${paste $tname Discriminant} { match self { $( $vpat => ${paste $tname Discriminant}::$vname, ) } } $( fn ${paste is_ ${snake_case $vname}} (&self) -> bool { self.discriminant() == ${paste $tname Discriminant} ::$vname } ) } } ``` Here we see a couple of new constructs. First, we're using [`${paste}`](crate::doc_reference#-paste---identifier-pasting) to glue several identifiers together into one. When we say `${paste $tname Discriminant}`, we are generating a new identifier from `$tname` (the type name) and the word Discriminant. So if the type name is `Foo`, the new type will be called `FooDiscriminant`. Second, we're using [`${snake_case}`](crate::doc_reference#case-changing) to transform an identifier into `snake_case` (that is, lowercase words separated by underscores). We use this to turn the name of each variant (`$vname`) into a name suitable for use in a function name. So if a variant is called `ExampleVariant`, `${snake_case $vname}` will be `example_variant`, and `${paste is_ ${snake_case $vname}}` will be `is_example_variant`. There are [other case-changers](crate::doc_reference#case-changing): * `${pascal_case my_ident}` becomes `MyIdent`. You can also write this as `${upper_camel_case ..}`. * `${lower_camel_case my_ident}` becomes `myIdent`. * `${shouty_snake_case MyIdent}` becomes `MY_IDENT`. * `${snake_case MyIdent}` becomes `my_ident`, as you've already seen. You can abbreviate `${paste ...}` as `$<...>`. #### A note on syntax In this last section, you've seen a new syntax for the first time. Both `${paste ident ident..}` and `${snake_case ident}` are special cases of the following meta-syntax, which `derive_adhoc` uses everywhere: [`${KEYWORD ARGS.. }`](crate::doc_reference#named-and-positional-template-arguments-to-expansions-and-conditions) In fact, if you want, you can use this format for all of the expansion macros you have already seen: `$ttype` is just a shortened form for `${ttype}`, `$fname` is just `${fname}`, and so on. Some keywords, including some of those we've already seen, can take named arguments. The syntax for this is: `${KEYWORD ARGNAME=VALUE ARGNAME=VALUE...}` > For example, we can use this syntax to give optional arguments to `$vpat`; > see the template syntax reference for more information. If you ever need to write a literal `$` (say, if you want to confuse yourself by making derive-adhoc-generated pattern macros) you can write [`$$`](crate::doc_reference#dollar-dollar). ## Another example: Defining a constructor function. In this section, we'll be using another example to demonstrate more of what `derive_adhoc` can do. We'll be building a `Constructor` template to define a `new()` function for a struct, without having to write out all of its arguments. Let's start with the following (struct-only) template: ``` # use derive_adhoc::define_derive_adhoc; define_derive_adhoc! { Constructor = impl<$tgens> $ttype where $twheres { pub fn new( $( $fname: $ftype , ) ) -> Self { Self { $( $fname , ) } } } } ``` When you apply the above template to a type like this: ``` # use derive_adhoc::define_derive_adhoc; # define_derive_adhoc! { # Constructor = # # impl<$tgens> $ttype where $twheres { # pub fn new( $( $fname: $ftype , ) ) -> Self { # Self { # $( $fname , ) # } # } # } # } use derive_adhoc::Adhoc; #[derive(Adhoc)] #[derive_adhoc(Constructor)] struct Ex { a: f64, b: A, c: String } ``` You'll get a constructor like this: ``` # struct Ex { a: f64, b: A, c: String } impl Ex { pub fn new( a: f64, b: A, c: String ) -> Self { Self { a, b, c } } } ``` So far, there aren't any new techniques at work here. We'll add some more down below. ### Marking a template's limitations The template above doesn't work for enumerations. If you try to apply it to one, you'll get a not-entirely-helpful error message. In earlier examples, we've shown how to make templates that apply to enums as well as structs. But let's say that in this case, we want our template to be struct-only. We can tell `derive_adhoc` about this restriction, to help it generate more useful error messages: ``` # use derive_adhoc::define_derive_adhoc; define_derive_adhoc! { Constructor for struct = // (1) impl<$tgens> $ttype where $twheres { pub fn new( $( $fname: $ftype , ) ) -> Self { Self { $( $fname , ) } } } } ``` (Note the use of [`for struct`](crate::doc_reference#for-struct-for-enum-for-union--insist-on-a-particular-driver-kind) above at `// (1)`.) Now if we try to apply our template to an enum, we'll get a more useful error: ```text,ignore error: template defined for struct, but applied to enum ``` ### Debugging templates When writing complex templates, it can sometimes be hard to figure out compiler errors. derive-adhoc has some features which can help: #### Syntax checking the expansion You can [tell derive-adhoc what your macro is supposed to produce](crate::doc_reference#expect-items-expect-expr--syntax-check-the-expansion): ``` # use derive_adhoc::{Adhoc, define_derive_adhoc}; define_derive_adhoc! { Constructor for struct, expect items = impl<$tgens> $ttype where $twheres { // ... } } # #[derive(Adhoc)] # #[derive_adhoc(Constructor)] # struct Test; ``` If the expansion fails to syntax check, you'll get not only an error pointing at the part of the template or structure which seems wrong, but also error messages pointing into a *copy of the actual expansion*. Hopefully you'll be able to see what's wrong, there. If your macro is supposed to expand to an expression, you can write `expect expr` instead of `expect items`. Alternatively, you can request this syntax check [when you invoke the macro](Adhoc#expansion-options): ``` # use derive_adhoc::{Adhoc, define_derive_adhoc}; # define_derive_adhoc! { Constructor = } #[derive(Adhoc)] #[derive_adhoc(Constructor[expect items])] struct MyStruct { /* ... */ } ``` #### Seeing a copy of the expansion Sometimes, the expansion syntax checks, but is wrong in some other way. You can ask derive-adhoc to simply [print a copy of the expansion](crate::doc_reference#dbg--print-the-expansion-to-stderr-for-debugging). ``` # use derive_adhoc::{Adhoc, define_derive_adhoc}; # define_derive_adhoc! { Constructor = } #[derive(Adhoc)] #[derive_adhoc(Constructor[dbg])] struct MyStruct { /* ... */ } ``` You'll see the compiler print something like this: ```ignore ---------- derive-adhoc expansion of Constructor for MyStruct (start) ---------- impl<> MyStruct where { pub fn new(...) { ... } } ---------- derive-adhoc expansion of Constructor for MyStruct (end) ---------- ``` Like the `dbg!` macro, you don't want to leave this in your production code. ### Working with visibility Our `Constructor` template above doesn't really make sense if it's applied to a non-public type: Rust may even complain that we're declaring a public function that ruturns a private type! Let's fix this, and have it give our constructor the same visibility as the type itself: ``` # use derive_adhoc::define_derive_adhoc; define_derive_adhoc! { Constructor for struct = impl<$tgens> $ttype where $twheres { $tvis fn new( $( $fname: $ftype , ) ) -> Self { Self { $( $fname , ) } } } } ``` Here instead of saying `pub fn new`, we said `$tvis fn new`. The [`$tvis`](crate::doc_reference#fvis-tvis-fdefvis--visibility) keyword will expand to the visibility of the top-level type. There is a similar similar [`$fvis`](crate::doc_reference#fvis-tvis-fdefvis--visibility) that expands to the visibility of the current field. (Since enums variants are always visible, there is no `$vvis`.) ### 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. We could do this as follows: ``` # use derive_adhoc::define_derive_adhoc; define_derive_adhoc! { Constructor for struct = impl<$tgens> $ttype where $twheres { pub fn ${tmeta(newfn)} // (1) ( $( $fname: $ftype , ) ) -> Self { Self { $( $fname , ) } } } } use derive_adhoc::Adhoc; #[derive(Adhoc)] #[derive_adhoc(Constructor)] #[adhoc(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(newfn)}`. This tells the template to look for an [`#[adhoc(newfn="...")]`](Adhoc#adhoc-attribute) attribute on the type, and to use the value of that attribute in place of the keyword. If we want our attribute to be more namespaced, we can instead say something like `${tmeta(Constructor(newfn = "..."))}`. If we do, the template will look for an attribute like `#[adhoc(Constructor(newfn = "..."))]`. The [`$tmeta`](crate::doc_reference#tmeta-vmeta-fmeta--adhoc-attributes) keyword that we used here tells the template to look at the `#[adhoc]` attributes for the _type_. We can, instead, use [`$vmeta`](crate::doc_reference#tmeta-vmeta-fmeta--adhoc-attributes) to look for `#[adhoc]` attributes for the current _variant_, or [`$fmeta`](crate::doc_reference#tmeta-vmeta-fmeta--adhoc-attributes) to to look for `#[adhoc]` attributes for the current _field_. ### Getting started with conditionals In the example above, we made it possible to rename the "new" function generated by our template. But our approach is flawed: if the user _doesn't_ provide the `#[adhoc(newfn)]` adhoc attribute, the template won't make a function name at all! Let's show how to fix that: ``` # use derive_adhoc::define_derive_adhoc; define_derive_adhoc! { Constructor for struct = impl<$tgens> $ttype where $twheres { pub fn ${if tmeta(newfn) { ${tmeta(newfn)} } else { new } } // (1) ( $( $fname: $ftype , ) ) -> Self { Self { $( $fname , ) } } } } ``` Have a look at the line marked with `// (1)`. It introduces a new concept: _conditional expansion_. The [`${if ...}`](crate::doc_reference#if-cond1----else-if-cond2----else-----conditional) keyword checks whether a given _condition_ is true. If it is, then it expands to one of its arguments. Otherwise, it expands to an "else" argument (if any). > Also, you can chain `$if`s, as in > `${if COND1 { ... } else if COND2 { ... } else { ... }` > and so on! Here, the condition is [`tmeta(newfn)`](crate::doc_reference#fmetaname-vmetaname-tmetaname--adhoc-attributes). That condition is true if the current type has an `#[adhoc(newfn)]` attribute, and false otherwise. There are also `vmeta` and `fmeta` attributes to detect `#[adhoc(..)]` attributes on variants and fields respectively. ### More complicated conditionals Frequently, we'd like our template to behave in different ways different fields. For example, let's suppose that we want our template to be able to set fields to their default values, and not take them as arguments. We could do this with an explicit conditional for each field: ``` # use derive_adhoc::define_derive_adhoc; define_derive_adhoc! { Constructor = impl<$tgens> $ttype where $twheres { pub fn new ( $( ${when not(fmeta(Constructor(default))) } // (1) $fname: $ftype , ) ) -> Self { Self { $( $fname: ${if fmeta(Constructor(default)) { Default::default() } else { $fname } } , ) } } } } use derive_adhoc::Adhoc; #[derive(Adhoc)] #[derive_adhoc(Constructor)] struct Foo { #[adhoc(Constructor(default))] s: Vec, n: u32, } ``` Here we're using a new construct: [`$when`](crate::doc_reference#when-condition--filtering-out-repetitions-by-a-predicate). It's only valid inside a loop like `$( ... )`. It causes the output of the loop to be surpressed whenever the condition is not true. The condition in this cases is `not(fmeta(Constructor(default)))`. You've seen `fmeta` before; [`not`](crate::doc_reference#false-true-notcondition-anycond1cond2-allcond1cond2--boolean-logic) is just how we express negation. All together, this `$when` keyword causes each field that has `#[adhoc(Constructor(default))]` applied to it to be omitted from the list of arguments to the `new()` function. You can use other boolean operators in conditions too: there is an [`any(...)`](crate::doc_reference#false-true-notcondition-anycond1cond2-allcond1cond2--boolean-logic) that is true whenever at least one of its arguments is true, and an [`all(...)`](crate::doc_reference#false-true-notcondition-anycond1cond2-allcond1cond2--boolean-logic) that is true when _all_ of its arguments are true. ## Other features derive-adhoc has many more features, that aren't yet explained in this tutorial. For example: * [`is_enum`, `is_struct`, `is_union`](crate::doc_reference#is_struct-is_enum-is_union); [`v_is_unit`, `is_named`, `is_tuple`](crate::doc_reference#v_is_unit-v_is_tuple-v_is_named); [`fvis`, `tvis`](crate::doc_reference#fvis-tvis-fdefvis--test-for-public-visibility): and [`approx_equal`](crate::doc_reference#approx_equalarg1-arg2--equality-comparison-token-comparison): more conditions for dealing with various cases by hand. * [`$tdefkwd`](crate::doc_reference#tdefkwd--keyword-introducing-the-new-data-structure); [`$tdeftype`](crate::doc_reference#ftype-vtype-ttype-tdeftype--types); [`$fdefvis`](crate::doc_reference#fvis-tvis-fdefvis--visibility); [`$fdefgens`](crate::doc_reference#tgens-tgnames-twheres-tdefgens--generics); and [`$tdefvariants`, `$vdefbody`, `$fdefine`](crate::doc_reference#tdefvariants-vdefbody-fdefine--tools-for-defining-types) for defining a new data structure in terms of features of the input data structure, and [`$Xattrs`](crate::doc_reference#fattrs--vattrs--tattrs---other-attributes) for passing through attributes. * [`${select1}`](crate::doc_reference#select1-cond1----else-if-cond2----else-----expect-precisely-one-predicate) can help with writing careful templates that will reject incoherent inputs. * [`#{define }` and `${defcond }`](crate::doc_reference#define--defcond---user-defined-expansions-and-conditions) to make user-defined reuseable template keywords, to save repetition in templates. Full details are in the [reference], which also has a brief example demonstrating each construct. [reference]: crate::doc_reference [README]: crate [`paste`]: https://docs.rs/paste/latest/paste/