# Heavy Alchemy - the black arts of transmutation, wrapped for your safe usage and enjoyment. `hv-alchemy` is a set of traits and types which maintain a global runtime registry (static is not possible at the moment, but the `linkme` crate might come into play at some point) of trait object vtables and other type info such as sizes, alignments, destructors, and more. It requires Rust nightly for the `ptr_metadata`, `unsize`, and `arbitrary_self_types` features. A few of the things this crate allows you to do: - At runtime, ask "do we know if this type implements this object-safe trait?" - Downcast a `dyn AlchemicalAny` to a concrete sized type `T` - *Dyn*cast a `dyn AlchemicalAny` to an unsized trait object type `dyn Trait` - Copy and clone types without compile-time `Copy` and `Clone` bounds (requiring runtime-registered `Copy` and `Clone` implementations) - Extract `Send`, `Sync`, `Copy`, and `Clone` constraints which can be used during compile-time and are conditionally accessed during run-time, for specializing behavior according to traits some type implements - Extend the `TypeTable` (vtable registry) for any applicable type at any time with any trait it is statically known to implement - Access "types" at runtime by casting `Type` to `Box` or any other trait you implement for it - Access the `Default::default` function pointer item as a `fn() -> T` for some type which implements `Default` (checking at runtime and without a compile-time `Default` bound) - Given a `*const dyn AlchemicalAny`, try to clone it into a fresh allocation using the `Layout` stored in the type table provided by `dyn AlchemicalAny` Please use irresponsibly. `hv-alchemy` is `no_std` compatible, but requires `alloc`. ## Caveats We cannot currently at compile-time register all the different things that some type implements w/ the Alchemy registry. As such you basically have to supply some kind of hook in your APIs (if you're relying on Alchemy) which encourages/provides a way to add relevant trait objects to the `TypeTable`s you're interested in. Something like `linkme` or the currently broken `inventory` crate are capable of doing something somewhat like this but *they cannot universally quantify over types*, as that would require monomorphizing a `forall T.` bound into a bunch of `Type::::of().add::<...>()` calls, and for very good reasons which may or may not be obvious to you, Rust currently has no way to do this. ## How it works Alchemy keeps a global static `HashMap` of `TypeId`s to `&'static TypeTable`s, which are created through `Box::leak` (at some point this might be switched to a global `Bump` arena or similar.) These `TypeTable`s contain again, `HashMap`s of `TypeId` to `&'static DynVtable`s; a `DynVtable` represents the vtable of some trait object `dyn SomeTrait` for some type `SomeType`. Specifically, if you want to see if some object implements `fmt::Debug`, Alchemy lookups go something like this: - Get the `TypeTable` of the object. If we're trying to dyncast a trait object `dyn AlchemicalAny`, it's as easy as calling the `.type_table()` method. Otherwise, we could use `Type::::of()`, if know `T` statically (and just don't want a static `fmt::Debug` bound.) - If we use `Type::::of()`, Alchemy takes the `TypeId` of `T` and uses it to look for its `TypeTable` in the global registry. If it's not there, it creates an empty one. - Take the `TypeId` of `dyn fmt::Debug`, and use that to look for the corresponding `DynVtable` in the `vtables` map of the `TypeTable`. - If we find a `DynVtable` there, we know by invariants that its implementor type will be the type of the thing we're trying to check, and that its trait object type will be `dyn fmt::Debug`. We can then assume it is safe to use `DynVtable::to_dyn_object_pointer` on our original reference/value to convert it to an `&dyn fmt::Debug`. Or if all we wanted to know was whether it *did* implement `Debug`, we have our answer. ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ## Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.