hv-alchemy 0.1.0

Heavy Alchemy - the black arts of transmutation, wrapped for your safe usage and enjoyment
# 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<T>` to `Box<dyn AlchemicalAny>` 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::<T>::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::<T>::of()`, if
  know `T` statically (and just don't want a static `fmt::Debug` bound.)
  - If we use `Type::<T>::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.