# Late Struct

[](https://docs.rs/late-struct/latest/late_struct/)
[](#license)
[](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).