late_struct/lib.rs
1//! Late-bound structure definitions.
2//!
3//! This crate exposes the [`late_struct!`] macro, which defines a structure whose set of fields
4//! can be extended by any crate within a compiled artifact using the [`late_field!`] macro.
5//! Unlike regular structures, dependents on the crate which originally defined the structure are
6//! allowed to extend it. Additionally, the structure we defined can be instantiated in any crate
7//! using a [`LateInstance`], even if dependents of that crate are still extending it.
8//!
9//! # Basic Usage
10//!
11//! For example, let's say we had a crate hierarchy where "`dependent` depends on `dependency`."
12//!
13//! In `dependency`, we could define a new late-struct marker using the [`late_struct!`] macro...
14//!
15//! ```
16//! use late_struct::late_struct;
17//!
18//! // Marker type for our application context.
19//! // Any type could be used here.
20//! pub struct AppContext;
21//!
22//! late_struct!(AppContext);
23//! ```
24//!
25//! ...and then, in `dependent`, we could proceed to add a field to it using the [`late_field!`]
26//! macro:
27//!
28//! ```
29//! # mod dependency {
30//! # use late_struct::late_struct;
31//! # pub struct AppContext;
32//! # late_struct!(AppContext);
33//! # }
34//! use late_struct::late_field;
35//!
36//! use dependency::AppContext;
37//!
38//! #[derive(Debug, Default)] // Type must implement `Debug`, `Default`, and live for `'static`.
39//! pub struct MyField(Vec<u32>);
40//!
41//! late_field!(MyField[AppContext]);
42//! ```
43//!
44//! ...just note that, by default, the field value must implement [`Debug`](std::fmt::Debug),
45//! [`Default`], and live for `'static`.
46//!
47//! We can then refer to the structure we've created with a [`LateInstance`]. For example, back in
48//! `dependency`, we can write...
49//!
50//! ```
51//! # use late_struct::late_struct;
52//! # pub struct AppContext;
53//! # late_struct!(AppContext);
54//! use late_struct::LateInstance;
55//!
56//! pub fn create_my_instance() -> LateInstance<AppContext> {
57//! LateInstance::new()
58//! }
59//! ```
60//!
61//! ...even though downstream crates such as `dependent` are still adding fields to it. Finally, we
62//! can access fields using the [`LateInstance::get`] and [`LateInstance::get_mut`] methods. For
63//! example, in the `dependent` crate, we could write...
64//!
65//! ```
66//! # mod dependency {
67//! # use late_struct::{late_struct, LateInstance};
68//! #
69//! # pub struct AppContext;
70//! #
71//! # late_struct!(AppContext);
72//! #
73//! # pub fn create_my_instance() -> LateInstance<AppContext> {
74//! # LateInstance::new()
75//! # }
76//! # }
77//! use dependency::{AppContext, create_my_instance};
78//! # use late_struct::late_field;
79//! #
80//! # #[derive(Debug, Default)] // Type must implement `Default` and live for `'static`.
81//! # pub struct MyField(Vec<u32>);
82//! #
83//! # late_field!(MyField[AppContext]);
84//!
85//! pub fn example() {
86//! let mut instance = create_my_instance();
87//!
88//! instance.get_mut::<MyField>().0.push(1);
89//! instance.get_mut::<MyField>().0.push(2);
90//! instance.get_mut::<MyField>().0.push(3);
91//!
92//! eprintln!("Our numbers are {:?}", instance.get::<MyField>());
93//! }
94//! ```
95//!
96//! See the documentation of [`LateInstance`] for more ways to access the instance.
97//!
98//! Note that the "key type" used to refer to a given field can be distinct from its value type. For
99//! example, in the previous snippet, we could make `MyField` a zero-sized marker type and set it up
100//! to refer to a value of type `Vec<u32>` instead. We do this by changing our `late_field!` macro
101//! invocation like so...
102//!
103//! ```
104//! # mod dependency {
105//! # use late_struct::{late_struct, LateInstance};
106//! #
107//! # pub struct AppContext;
108//! #
109//! # late_struct!(AppContext);
110//! #
111//! # pub fn create_my_instance() -> LateInstance<AppContext> {
112//! # LateInstance::new()
113//! # }
114//! # }
115//! use dependency::{AppContext, create_my_instance};
116//! # use late_struct::late_field;
117//! #
118//!
119//! // The `#[non_exhaustive]` attribute helps ensure that other crates don't
120//! // accidentally try to instantiate what should just be a marker type.
121//! #[non_exhaustive]
122//! pub struct MyField;
123//!
124//! late_field!(MyField[AppContext] => Vec<u32>);
125//! // ^^^^^^^^^^^ this is how we specify the
126//! // field's value type explicitly.
127//!
128//! pub fn example() {
129//! let mut instance = create_my_instance();
130//!
131//! // Notice that we're now accessing the `&mut Vec<u32>` directly
132//! // rather than the `MyField` wrapper.
133//! instance.get_mut::<MyField>().push(1);
134//! instance.get_mut::<MyField>().push(2);
135//! instance.get_mut::<MyField>().push(3);
136//!
137//! eprintln!("Our numbers are {:?}", instance.get::<MyField>());
138//! }
139//! ```
140//!
141//! # Advanced Usage
142//!
143//! By default, all fields of a given struct are required to implement [`Debug`](std::fmt::Debug),
144//! [`Default`], and `'static`. These requirements, however, can be changed on a per-struct basis.
145//! For instance, we can remove the `Debug` requirement and instead require [`Send`], [`Sync`], and
146//! a custom trait `Reflect` with the following [`late_struct!`] definition...
147//!
148//! ```
149//! use late_struct::late_struct;
150//!
151//! trait Reflect {
152//! fn say_hi(&self);
153//! }
154//!
155//! struct MyStruct;
156//!
157//! late_struct!(MyStruct => dyn 'static + Reflect + Send + Sync);
158//! // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
159//! // field value types must upcast to this type
160//! ```
161//!
162//! The only mandatory trait bounds on a field are that it have a [`Default`] initializer, be
163//! [`Sized`], and live for `'static`.
164//!
165//! We can then enumerate these fields at runtime using the [`LateInstance::fields`] method and
166//! access those fields' erased values using the [`LateInstance::get_erased`], and
167//! [`LateInstance::get_erased_mut`] methods like so...
168//!
169//! ```
170//! # use late_struct::{late_field, late_struct, LateInstance};
171//! # use std::sync::Arc;
172//! #
173//! # trait Reflect {
174//! # fn say_hi(&self);
175//! # }
176//! #
177//! # struct MyStruct;
178//! #
179//! # late_struct!(MyStruct => dyn 'static + Reflect + Send + Sync);
180//! #
181//! #[derive(Default)]
182//! struct MyField;
183//!
184//! impl Reflect for MyField {
185//! fn say_hi(&self) {
186//! println!("Hello!");
187//! }
188//! }
189//!
190//! late_field!(MyField[MyStruct]);
191//!
192//! fn say_greetings_on_a_thread(instance: Arc<LateInstance<MyStruct>>) {
193//! std::thread::spawn(move || {
194//! for field in instance.fields() {
195//! instance.get_erased(field).say_hi();
196//! }
197//! })
198//! .join()
199//! .unwrap()
200//! }
201//! #
202//! # say_greetings_on_a_thread(Arc::new(LateInstance::new()));
203//! ```
204//!
205//! Struct members can also be made to satisfy non-[`dyn` compatible] standard traits such
206//! as [`Eq`], [`Hash`], and [`Clone`] by making the members implement the [`DynEq`], [`DynHash`],
207//! and [`DynClone`] traits respectively. This lets us write, for instance...
208//!
209//! ```
210//! use std::{fmt::Debug, collections::HashSet};
211//! use late_struct::{late_field, late_struct, DynEq, DynHash, DynClone, LateInstance};
212//!
213//! trait MyStructMember: Debug + DynEq + DynHash + DynClone {}
214//!
215//! impl<T> MyStructMember for T
216//! where
217//! T: Debug + DynEq + DynHash + DynClone,
218//! {
219//! }
220//!
221//! struct MyStruct;
222//!
223//! late_struct!(MyStruct => dyn MyStructMember);
224//!
225//! #[derive(Debug, Clone, Hash, Eq, PartialEq, Default)]
226//! struct MyField(u32);
227//!
228//! late_field!(MyField[MyStruct]);
229//!
230//! fn demo() {
231//! // The struct implements `Default`...
232//! let my_instance = LateInstance::<MyStruct>::default();
233//!
234//! // ...debug...
235//! eprintln!("{my_instance:?}");
236//!
237//! // ...clone...
238//! let my_instance_2 = my_instance.clone();
239//!
240//! // ...eq...
241//! assert_eq!(my_instance, my_instance_2);
242//!
243//! // ...and hash!
244//! let mut map = HashSet::new();
245//!
246//! assert!(map.insert(my_instance));
247//! assert!(!map.insert(my_instance_2));
248//! }
249//! # demo();
250//! ```
251//!
252//! # Internals
253//!
254//! Internally, each field we define with [`late_field!`] creates a `static` containing a
255//! [`LateFieldDescriptor`] and uses [`linkme`] (or `inventory` on WebAssembly) to add it to a
256//! global list of all fields in the crate. When our first [`LateInstance`] is instantiated, all
257//! these `LateFieldDescriptor`s are collected and laid out into a structure at runtime, with each
258//! fields' offset being written back into an `AtomicUsize` in the `LateFieldDescriptor`.
259//!
260//! From there, structure instantiation and field fetching work more-or-less like they would with a
261//! regular structure. `LateInstance` creates one big heap allocation for the structure it
262//! represents and initializes each field accordingly. To access a field, all we have to do is
263//! offset the structure's base pointer by the dynamically-initialized offset stored in the field's
264//! `LateFieldDescriptor`, making field accesses extremely cheap.
265//!
266//! Many of these internals are exposed to the end user. See [`LateStructDescriptor`] and
267//! [`LateFieldDescriptor`] (which you can obtain from the [`LateStruct::descriptor`] and
268//! [`LateField::descriptor`] methods respectively) to learn about various options for reflecting
269//! upon the layout of a structure.
270//!
271//! [`dyn` compatible]: https://doc.rust-lang.org/1.87.0/reference/items/traits.html#r-items.traits.dyn-compatible
272
273#![forbid(missing_docs)]
274
275mod descriptor;
276pub use self::descriptor::*;
277
278mod init;
279pub use self::init::*;
280
281mod instance;
282pub use self::instance::*;
283
284mod std_ops;
285pub use std_ops::*;
286
287mod traits;
288pub use self::traits::*;