Working with structs and fields

Let's start by imagining that we had to write Clone from scratch for a simple structure like this:

#![allow(unused)]
fn main() {
struct GiftBasket {
   n_apples: u8,
   n_oranges: u8,
   given_from: Option<String>,
   given_to: String,
}
}

We'd probably write something like this:

#![allow(unused)]
fn main() {
struct GiftBasket {
  n_apples: u8,
  n_oranges: u8,
  given_from: Option<String>,
  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 the compiler should be smart enough to optimize the clone() away.)

If you imagine generalizing this to any simple struct with named fields, you might come up with a pseudocode template like this one:

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-deftly template:

#![allow(unused)]
fn main() {
use derive_deftly::define_derive_deftly;

define_derive_deftly! {
    MyClone:

    impl Clone for $ttype {
        fn clone(&self) -> Self {
            Self {
                $( $fname : self.$fname.clone() , )
            }
        }
    }
}
}

Let's look at that template piece by piece. Certain marked "expansion keywords" in the contents (those starting with $) are replaced with a value that depends on the struct we are applying the template to.

You've already seen $ttype ("top-level type"): it expands to the type on which you are applying the macro. There are two new pieces of syntax here, though: $( ... ) and $fname.

In derive-deftly 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 once for each field.

The $fname ("field name") expansion means "the name of the current field". Which field is that? 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.)

An aside on naming

At this point, you've seen expansions with names like $ttype and $fname, and you may be wondering where these names come from. If you want to know more, you can skip ahead to the section on naming conventions.