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:

#![allow(unused)]
fn main() {
use std::sync::Arc;
struct Indirect<T>(Arc<T>, u16);
}

If you try to derive Clone on it, the compiler will generate code something like this:

impl<T: Clone> Clone for Indirect<T> { ... }

But that T: Clone constraint isn't strictly necessary: Arc<T> always implements Clone, so your struct could have been be Clone unconditionally.

But using derive-deftly, you can define a template that derives Clone only for the cases where the actual required constraints are met:

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

    impl<$tgens> Clone for $ttype
    where $twheres
          // This is the new part:
          $( $ftype : Clone , )
    {
        // (The rest is as before...)
        fn clone(&self) -> Self {
            match self {
                $(
                    $vpat => $vtype {
                        $(
                            $fname: $fpatname.clone(),
                        )
                    },
                )
            }
        }
    }
}
use std::{sync::Arc, fmt::Debug} ;
#[derive(Deftly)]
#[derive_deftly(MyClone[dbg])]
struct Example<T> where T: Debug {
  arc: Arc<T>
}
#[derive(Deftly)]
#[derive_deftly(MyClone[dbg])]
struct Example2<T> {
  arc: Arc<T>
}
}

Here, we are using $ftype. ("field type") to get the actual type of each 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:

impl<T> Clone for Direct
where
    T: Clone,
    T: Clone,
    String: Clone
{
    ...
}

What about that comma?

If you're paying close attention, you might have thought we had a syntax error above when we didn't use an explicit comma after where $twheres.

This time, derive_deftly has exactly one piece of cleverness at work. It makes sure that either $twheres is empty, or that it ends with a comma. That way, when your template expands where $twheres $( $ftype : Clone , ) it won't produce where U: Debug + Clone T: Clone(which is a syntax error) orwhere ,` (which is also a syntax error).

A further note on repetition

Note that when we define our additional where clauses above, we said where $( $ftype: Clone, ) at the top level. We didn't have to specify separate of repetition for variants and fields: if we have only $ftype in a top-level repetition, derive_deftly will iterate over all fields in all variants.

Sometimes, if you do something subtle, derive-deftly may not be able to figure out what you're trying to repeat over. You can use ${for fields {...}} or ${for variants {...}} to specify explicitly what you want to repeat. So, above, instead, we could have written

#![allow(unused)]
fn main() {
use derive_deftly::{Deftly, derive_deftly_adhoc};
#[derive(Deftly)]
#[derive_deftly_adhoc]
enum TestCase<A> { Variant(A) }
derive_deftly_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. For example, we could have used ${for} to write our MyClone example more explicitly like this:

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

    impl<$tgens> Clone for $ttype
    where $twheres
          ${for fields { $ftype: Clone , }}
    {
        fn clone(&self) -> Self {
            match self {
                ${for variants {
                    $vpat => $vtype {
                        ${for fields {
                            $fname: $fpatname.clone(),
                        }}
                    },
                }}
            }
        }
    }
}
}