phenotype_internal/
lib.rs

1// TODO: add examples
2
3/// This trait represents the behavior of an `enum`/tagged union.
4/// **Note**: it should only be implemented with `#[derive(Phenotype)]`
5/// # Safety
6/// This trait is marked unsafe because _extreme_ care must be taken to implement
7/// it correctly. In particular, the `reknit` method can cause undefined behavior
8/// if called with invalid inputs. Manual implementation of the trait is heavily
9/// discouraged, but there may be cases (e.g. `const` generics) where it is necessary.
10pub unsafe trait Phenotype {
11    /// The number of variants of the enum.
12    const NUM_VARIANTS: usize;
13
14    /// The number of bits needed to represent every variant of the enum.
15    /// For example, if the enum has 4 variants, then two bits are needed.
16    const BITS: usize;
17
18    /// The number of bits `Phenotype` uses to represent and instance of a type.
19    /// If the type `Phenotype` is being implemented for is generic,
20    /// this will be `None`, as sizes may vary accross different
21    /// generic parameters. For example, `Type<usize>` could be differently
22    /// sized than `Type<[usize; 4]>`
23    const PEAPOD_SIZE: Option<usize>;
24
25    /// Whether using `Phenotype` produces a more compact representation.
26    /// Will be `Some(true)` if implementations are the same size.
27    /// If the type `Phenotype` is being implemented for is generic,
28    /// this will be `None`, as sizes may vary accross different
29    /// generic parameters. For example, `Type<usize>` could be differently
30    /// sized than `Type<[usize; 4]>`
31    const IS_MORE_COMPACT: Option<bool>;
32
33    /// A type that represents all the data an enum can contain.
34    /// This should be a union whose fields each represent a particular
35    /// enum variant.
36    type Value;
37
38    /// Takes an enum variant and `cleave`s it into a two parts:
39    /// a tag, and an union representing the data the enum can hold.
40    /// If the enum variant doesn't hold data, `None` is returned as
41    /// the second tuple element.
42    /// **Note**: if the results of a call to `cleave` are not eventually
43    /// `reknit`ed, the destructor for the `cleave`ed enum will not run.
44    /// This can cause memory leaks. Types that manage heap memory often
45    /// implement cleanup and deallocation in their `Drop` implementations.
46    fn cleave(self) -> (usize, Self::Value);
47
48    /// Takes a tag and a value and recombines them into a proper
49    /// instance of an enum variant.
50    /// # Safety
51    /// Calling this function with incorrect
52    /// inputs can result in undefined behavior. The tag must always match
53    /// the state that the union is in.
54    ///
55    /// For example, consider the following example
56    /// ```
57    /// #[derive(Phenotype)]
58    /// enum UB {
59    ///     U(usize), // -> tag = 0
60    ///     B(bool)   // -> tag = 1
61    /// }
62    ///
63    /// // This is the type <UB as Phenotype>::Value
64    /// union Value {
65    ///     U: usize,
66    ///     B: bool
67    /// }
68    ///
69    /// use peapod::Phenotype;
70    /// fn main {
71    ///     let ub = UB::U(3);
72    ///     let (_, data) = ub.cleave();
73    ///     // ** DANGER **
74    ///     // We are interpreting 3 as a bool! That's undefined behavior.
75    ///     let BAD = <UB as Phenotype>::reknit(1, data);
76    /// }
77    /// ```
78    unsafe fn reknit(tag: usize, value: Self::Value) -> Self;
79}
80
81/// Some helpful methods for using `Phenotype`
82pub trait PhenotypeDebug: Phenotype {
83    /// Returns the tag that Phenotype uses internally
84    /// to identify the enum variant.
85    /// **Note**: this is different from the tag the compiler
86    /// uses or a discriminant that was manually specified. It
87    /// only has meaning in the context of `Phenotype`.
88    fn discriminant(&self) -> usize;
89
90    /// Takes a tag and returns a string that represents
91    /// the variant. For example, it might return something
92    /// like `Result::Ok` for 0 and `Result::Err` for 1 if `Phenotype`
93    /// was derived on the `Result` type.
94    fn debug_tag(tag: usize) -> &'static str;
95}