late-struct 0.1.0

Late-bound structure definitions.
Documentation

# Late Struct

![Version](https://img.shields.io/static/v1?label=version&message=0.1.0&color=informational)
[![docs.rs](https://img.shields.io/docsrs/late-struct)](https://docs.rs/late-struct/latest/late_struct/)
[![Crates.io License](https://img.shields.io/crates/l/late-struct)](#license)
[![Crates.io](https://img.shields.io/crates/v/late-struct)](https://crates.io/crates/late-struct)

Late-bound structure definitions.

This crate exposes the [`late_struct!`](https://docs.rs/late-struct/latest/late_struct/macro.late_struct.html) macro, which defines a structure whose set of fields
can be extended by any crate within a compiled artifact using the [`late_field!`](https://docs.rs/late-struct/latest/late_struct/macro.late_field.html) macro.
Unlike regular structures, dependents on the crate which originally defined the structure are
allowed to extend it. Additionally, the structure we defined can be instantiated in any crate
using a [`LateInstance`](https://docs.rs/late-struct/latest/late_struct/instance/struct.LateInstance.html), even if dependents of that crate are still extending it.

## Basic Usage

For example, let's say we had a crate hierarchy where "`dependent` depends on `dependency`."

In `dependency`, we could define a new late-struct marker using the [`late_struct!`](https://docs.rs/late-struct/latest/late_struct/macro.late_struct.html) macro...

``` rust
use late_struct::late_struct;

// Marker type for our application context.
// Any type could be used here.
pub struct AppContext;

late_struct!(AppContext);
```

...and then, in `dependent`, we could proceed to add a field to it using the [`late_field!`](https://docs.rs/late-struct/latest/late_struct/macro.late_field.html)
macro:

``` rust
use late_struct::late_field;

use dependency::AppContext;

pub struct MyField(Vec<u32>);

late_field!(MyField[AppContext]);
```

...just note that, by default, the field value must implement [`Debug`](std::fmt::Debug),
[`Default`](https://doc.rust-lang.org/stable/core/default/trait.Default.html), and live for `'static`.

We can then refer to the structure we've created with a [`LateInstance`](https://docs.rs/late-struct/latest/late_struct/instance/struct.LateInstance.html). For example, back in
`dependency`, we can write...

``` rust
use late_struct::LateInstance;

pub fn create_my_instance() -> LateInstance<AppContext> {
    LateInstance::new()
}
```

...even though downstream crates such as `dependent` are still adding fields to it. Finally, we
can access fields using the `LateInstance::get` and `LateInstance::get_mut` methods. For
example, in the `dependent` crate, we could write...

``` rust
use dependency::{AppContext, create_my_instance};

pub fn example() {
    let mut instance = create_my_instance();

    instance.get_mut::<MyField>().0.push(1);
    instance.get_mut::<MyField>().0.push(2);
    instance.get_mut::<MyField>().0.push(3);

    eprintln!("Our numbers are {:?}", instance.get::<MyField>());
}
```

See the documentation of [`LateInstance`](https://docs.rs/late-struct/latest/late_struct/instance/struct.LateInstance.html) for more ways to access the instance.

Note that the "key type" used to refer to a given field can be distinct from its value type. For
example, in the previous snippet, we could make `MyField` a zero-sized marker type and set it up
to refer to a value of type `Vec<u32>` instead. We do this by changing our `late_field!` macro
invocation like so...

``` rust
use dependency::{AppContext, create_my_instance};

// The `#[non_exhaustive]` attribute helps ensure that other crates don't
// accidentally try to instantiate what should just be a marker type.
pub struct MyField;

late_field!(MyField[AppContext] => Vec<u32>);
//                              ^^^^^^^^^^^ this is how we specify the
//                                          field's value type explicitly.

pub fn example() {
    let mut instance = create_my_instance();

    // Notice that we're now accessing the `&mut Vec<u32>` directly
    // rather than the `MyField` wrapper.
    instance.get_mut::<MyField>().push(1);
    instance.get_mut::<MyField>().push(2);
    instance.get_mut::<MyField>().push(3);

    eprintln!("Our numbers are {:?}", instance.get::<MyField>());
}
```

## Advanced Usage

By default, all fields of a given struct are required to implement [`Debug`](std::fmt::Debug),
[`Default`](https://doc.rust-lang.org/stable/core/default/trait.Default.html), and `'static`. These requirements, however, can be changed on a per-struct basis.
For instance, we can remove the `Debug` requirement and instead require [`Send`](https://doc.rust-lang.org/stable/core/marker/trait.Send.html), [`Sync`](https://doc.rust-lang.org/stable/core/marker/trait.Sync.html), and
a custom trait `Reflect` with the following [`late_struct!`](https://docs.rs/late-struct/latest/late_struct/macro.late_struct.html) definition...

``` rust
use late_struct::late_struct;

trait Reflect {
    fn say_hi(&self);
}

struct MyStruct;

late_struct!(MyStruct => dyn 'static + Reflect + Send + Sync);
//                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//                    field value types must upcast to this type
```

The only mandatory trait bounds on a field are that it have a [`Default`](https://doc.rust-lang.org/stable/core/default/trait.Default.html) initializer, be
[`Sized`](https://doc.rust-lang.org/stable/core/marker/trait.Sized.html), and live for `'static`.

We can then enumerate these fields at runtime using the `LateInstance::fields` method and
access those fields' erased values using the `LateInstance::get_erased`, and
`LateInstance::get_erased_mut` methods like so...

``` rust
struct MyField;

impl Reflect for MyField {
   fn say_hi(&self) {
       println!("Hello!");
   }
}

late_field!(MyField[MyStruct]);

fn say_greetings_on_a_thread(instance: Arc<LateInstance<MyStruct>>) {
    std::thread::spawn(move || {
        for field in instance.fields() {
             instance.get_erased(field).say_hi();
        }
    })
    .join()
    .unwrap()
}
```

Struct members can also be made to satisfy non-[`dyn` compatible](https://doc.rust-lang.org/1.87.0/reference/items/traits.html#r-items.traits.dyn-compatible) standard traits such
as [`Eq`](https://doc.rust-lang.org/stable/core/cmp/trait.Eq.html), [`Hash`](https://doc.rust-lang.org/stable/core/hash/macros/derive.Hash.html), and [`Clone`](https://doc.rust-lang.org/stable/core/clone/trait.Clone.html) by making the members implement the [`DynEq`](https://docs.rs/late-struct/latest/late_struct/std_ops/trait.DynEq.html), [`DynHash`](https://docs.rs/late-struct/latest/late_struct/std_ops/trait.DynHash.html),
and [`DynClone`](https://docs.rs/late-struct/latest/late_struct/std_ops/trait.DynClone.html) traits respectively. This lets us write, for instance...

``` rust
use std::{fmt::Debug, collections::HashSet};
use late_struct::{late_field, late_struct, DynEq, DynHash, DynClone, LateInstance};

trait MyStructMember: Debug + DynEq + DynHash + DynClone {}

impl<T> MyStructMember for T
where
    T: Debug + DynEq + DynHash + DynClone,
{
}

struct MyStruct;

late_struct!(MyStruct => dyn MyStructMember);

struct MyField(u32);

late_field!(MyField[MyStruct]);

fn demo() {
    // The struct implements `Default`...
    let my_instance = LateInstance::<MyStruct>::default();

    // ...debug...
    eprintln!("{my_instance:?}");

    // ...clone...
    let my_instance_2 = my_instance.clone();

    // ...eq...
    assert_eq!(my_instance, my_instance_2);

    // ...and hash!
    let mut map = HashSet::new();

    assert!(map.insert(my_instance));
    assert!(!map.insert(my_instance_2));
}
```

## Internals

Internally, each field we define with [`late_field!`](https://docs.rs/late-struct/latest/late_struct/macro.late_field.html) creates a `static` containing a
[`LateFieldDescriptor`](https://docs.rs/late-struct/latest/late_struct/descriptor/struct.LateFieldDescriptor.html) and uses [`linkme`](https://docs.rs/linkme/0.3.33//linkme/index.html) (or `inventory` on WebAssembly) to add it to a
global list of all fields in the crate. When our first [`LateInstance`](https://docs.rs/late-struct/latest/late_struct/instance/struct.LateInstance.html) is instantiated, all
these `LateFieldDescriptor`s are collected and laid out into a structure at runtime, with each
fields' offset being written back into an `AtomicUsize` in the `LateFieldDescriptor`.

From there, structure instantiation and field fetching work more-or-less like they would with a
regular structure. `LateInstance` creates one big heap allocation for the structure it
represents and initializes each field accordingly. To access a field, all we have to do is
offset the structure's base pointer by the dynamically-initialized offset stored in the field's
`LateFieldDescriptor`, making field accesses extremely cheap.

Many of these internals are exposed to the end user. See [`LateStructDescriptor`](https://docs.rs/late-struct/latest/late_struct/descriptor/struct.LateStructDescriptor.html) and
[`LateFieldDescriptor`](https://docs.rs/late-struct/latest/late_struct/descriptor/struct.LateFieldDescriptor.html) (which you can obtain from the `LateStruct::descriptor` and
`LateField::descriptor` methods respectively) to learn about various options for reflecting
upon the layout of a structure.

## Contributing

Contributions in any form (issues, pull requests, etc.) to this project must adhere to 
Rust's [Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct).

Unless you explicitly state otherwise, any contribution intentionally submitted for 
inclusion in `late-struct` by you shall be licensed as below, without any 
additional terms or conditions.

## License

This project is licensed under the [MIT License](LICENSE).