macro_rules! props {
    (
		// A lazy/const impl that will be promoted to `Deref` (also impls `EnumProp`)
		impl Deref for $enum_name:ty as $modifier:ident $prop_name:path { $($matching:tt)* }
	) => { ... };
    (
		// The lazy/const impl via inherent method (also impls `EnumProp`)
		impl $enum_name:ty : $fn_vis:vis fn $fn_name:ident as $modifier:ident $prop_name:path { $($matching:tt)* }
	) => { ... };
    (
		// The lazy/const impl `EnumProp` only
		impl EnumProp for $enum_name:ty as $modifier:ident $prop_name:path { $($matching:tt)* }
	) => { ... };
}
Expand description

Adds a property onto an enum

Const, Static, Lazy

This macro allows implement properties in three different ways:

  • as const, a constant
  • as static, a global variable
  • as lazy, a lazily initialized static

const and static are very similar, but have subtle difference: the property type put into a static must implement Send. However, with a static it is guaranteed that for each variant there is exactly one unique property value and thus a unique reference address. With const the compiler is allowed to merge properties (if they are equal) or to inline and instantiated the same logical property multiple times, i.e. the same logical property might be accessed via different reference addresses. In the very most cases, the actual reference address should not be of any concern and thus it is recommended to use const over static.

One notable use-case for static is when the property contains interior mutability. In these cases, const shouldn’t even compile.

lazy, on the other hand, is quite different from the const and static. While const and static require constant initialized values computed at compile-time, lazy allows to evaluate the value lazily at runtime, instead. However, this feature incurs some overhead at each access, because it must be checked that the value was indeed already initialized. And of course, the first access to a lazy value, will incur the additional delay to initialize the value.

Syntax

This macro comes with essentially three different syntaxes: to implement Deref (for the primary property), add an inherent access method (for secondary properties), or just implementing EnumProp onto it (e.g., if only used by generic code).

Implementing Deref

Syntax:

impl Deref for <ENUM> as (const|static|lazy) <PROPERTY> {
    <VARIANT> => {
        <FIELD> : <VALUE>,
        ...
    },
    ...
}

Example:

struct Prop { name: &'static str }
enum Foo {A}
props! {
    impl Deref for Foo as const Prop {
        Self::A => {
            name: "Foo",
        }
    }
}
// Direct access due to deref
assert_eq!(Foo::A.name, "Foo");

Implementing an inherent method

Syntax:

impl <ENUM> : <VIS> fn <FN_NAME> as (const|static|lazy) <PROPERTY> {
    <VARIANT> => {
        <FIELD> : <VALUE>,
        ...
    },
    ...
}

Example:

struct Prop { name: &'static str }
enum Foo {A}
props! {
    // Of course, an arbitrary function names can be used instead of `getter`
    impl Foo : fn getter as const Prop {
        Self::A => {
            name: "Foo",
        }
    }
}
// Access via inherent method
assert_eq!(Foo::A.getter().name, "Foo");

Implementing only EnumProp

Syntax:

impl EnumProp for <ENUM> as (const|static|lazy) <PROPERTY> {
    <VARIANT> => {
        <FIELD> : <VALUE>,
        ...
    },
    ...
}

Example:

struct Prop { name: &'static str }
enum Foo {A}
props! {
    impl EnumProp for Foo as const Prop {
        Self::A => {
            name: "Foo",
        }
    }
}
// Access via universal function call
use enumeraties::EnumProp;
assert_eq!(EnumProp::<Prop>::property(&Foo::A).name, "Foo");