Some difficulties when creating our Diff type
In the last section, we saw what we want
our derive_deftly(Diff)
template to do.
Here we'll analyze a few problems that we need to solve
in order to make it work.
These problems mostly derive from the fact
that we're trying to write a single template
that can either generate a struct
or an enum
,
depending on the type we're applying it to,
but they all take different forms.
Problem 1: What keyword do we use to define?
We want an expansion that gives us struct
for a struct
and enum
for an enum.
While we could trivially build one with
${if is_struct {struct} else {enum}}
,
it seems kind of verbose.
Problem 2: How do we declare the generics?
When we define our new FooDiff
struct,
we want it to have the same generics that were used
when we declared Foo
.
We might think of doing this with something like:
struct $<$tname Diff><$tgens> where $twheres { ... }
And that might be good enough for many purposes,
but it isn't quite right.
Here's why:
If Foo
is defined as struct Foo<L:Display=Bar>
,
then FooDiff
will only get defined as struct Foo<L:Display>
.
Thus the corresponding Diff
type for Foo
will not be FooDiff
, but rather FooDiff<Bar>
.
We need a way to declare generics along with their defaults.
Problem 3: Extra braces on the enum case.
For the enum case, we need our template to generate:
enum FooDiff { Variant { ... }, Variant { ... }, ... }
But for the struct case, we want:
struct FooDiff { ... }
Note that there are extra braces in the enum
case.
How can we generate those?
(We can't use ${if}
to insert a single brace,
since all the arguments to a derive-deftly
expansion
need to have balanced braces.)
Problem 4: Defining our structs and variants
Earlier
we saw
that when constructure and destructing,
we could pretend that tuple structs and tuple variants
were really braced
variants with members named 0
, 1
, 2
, and so on;
and that we could pretend that unit structs and unit variants
were just empty braced
structs.
But this approach doesn't work when declaring a struct or variant:
struct Foo {}
is not the same as struct Foo;
,
and struct Foo { 0: u32 }
is a syntax error!
So we will need a different approach. We'll need to generate braces and field names if the struct/variant has named fields; we'll need to generate parentheses and no field names if the struct/variant is a tuple; and we'll need to generate nothing at all if we have a unit struct/variant.
(Moreover, if we're generating a tuple struct or a unit struct, we'll need a trailing semicolon, but if we're generating any kind of a variant, we'll need a trailing comma.)
Problem 5: field visibility works differently among structs and enums
We decided above that we wanted each "difference" field to have the same visibility as the original field in our struct and enum.
Earlier,
we saw $fvis
as a way to get the visibility of a field.
But this won't work when we're declaring an enum
:
since every enum field is public,
$fvis
for an enum's fields always expands to pub
,
and Rust won't allow us to write
enum FooDiff {
Variant { pub a: u32; },
}