Skip to content

Commit 736edbe

Browse files
committed
Add section on "generics" to "README.md" file
1 parent 2160a48 commit 736edbe

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed

macros/README.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,97 @@ This attribute is recognized by the following variant-based derive macros:
291291
292292
- `Encapsulate`
293293
294+
## Generics
295+
296+
There is limited support for generic enums:
297+
298+
Variants using generic const/type parameters are always excluded when deriving generic traits with `enumcapsulate`'s derive macros.
299+
300+
The reason for this behavior is that implementing generic traits for variants that use any of the generic parameters of the enum tends to result in conflicting implementations in Rust, as shown by the following example program:
301+
302+
```rust
303+
use enumcapsulate::FromVariant;
304+
305+
pub struct VariantA;
306+
pub struct VariantB;
307+
308+
#[derive(FromVariant)]
309+
// error[E0119]: conflicting implementations of trait `FromVariant<VariantB>` for type `Enum<VariantB>`
310+
pub enum Enum<T> {
311+
Unit,
312+
Generic(T),
313+
NonGeneric(VariantB),
314+
}
315+
316+
fn main() {
317+
let _: Enum<VariantA> = Enum::from_variant(VariantA);
318+
let _: Enum<VariantA> = Enum::from_variant(VariantB);
319+
}
320+
```
321+
322+
The expanded version of the above makes it easier to see why: The compiler can't prove that `T` and `VariantB` are disjoint types.
323+
324+
```rust
325+
pub struct VariantA;
326+
pub struct VariantB;
327+
328+
pub enum Enum<T> {
329+
Unit,
330+
Generic(T),
331+
NonGeneric(VariantB),
332+
}
333+
334+
impl<T> FromVariant<T> for Enum<T> { // <- first implementation here
335+
fn from_variant(variant: T) -> Self {
336+
Self::Generic(variant)
337+
}
338+
}
339+
340+
// error[E0119]: conflicting implementations of trait `FromVariant<VariantB>` for type `Enum<VariantB>`
341+
impl<T> FromVariant<VariantB> for Enum<T> { // <- conflicting implementation for `Enum<VariantB>`
342+
fn from_variant(variant: VariantB) -> Self {
343+
Self::NonGeneric(variant)
344+
}
345+
}
346+
347+
fn main() {
348+
let _: Enum<VariantA> = Enum::from_variant(VariantA);
349+
let _: Enum<VariantA> = Enum::from_variant(VariantB);
350+
}
351+
```
352+
353+
So in order to avoid such pitfalls altogether `enumcapsulate`'s derive macros will skip `impl<T> FromVariant<T> for Enum<T>`, since it uses a generic type (or const) parameter of `Enum<T>`.
354+
355+
So all you have to do is provide your own non-generic implementations for specific type instances of your generic type yourself, filling any gaps left behind by the derive macro:
356+
357+
```rust
358+
use enumcapsulate::FromVariant;
359+
360+
pub struct VariantA;
361+
pub struct VariantB;
362+
363+
#[derive(FromVariant)]
364+
pub enum Enum<T> {
365+
Unit,
366+
Generic(T),
367+
NonGeneric(VariantB),
368+
}
369+
370+
// Notice how the trait is implemented on
371+
// a specific type of the `Enum<T>` kind,
372+
// rather than on the generic kind itself:
373+
impl From<VariantA> for Enum<VariantA> {
374+
fn from(value: VariantA) -> Self {
375+
Self::Generic(value)
376+
}
377+
}
378+
379+
fn main() {
380+
let _: Enum<VariantA> = Enum::from_variant(VariantA);
381+
let _: Enum<VariantA> = Enum::from_variant(VariantB);
382+
}
383+
```
384+
294385
## Documentation
295386

296387
Please refer to the documentation on [docs.rs](https://docs.rs/enumcapsulate-macros).

0 commit comments

Comments
 (0)