Coi provides an easy to use dependency injection framework. Currently, this crate provides the following:
coi::Inject
(trait) - a marker trait that indicates a trait or struct is injectable.coi::Provide
- a trait that indicates a struct is capable of providing a specific implementation of some injectable trait. This is generated for you if you usecoi::Inject
(derive), but can also be written manually.coi::Container
- a container to manage the lifetime of all dependencies. This is still in its early stages, and currently only supports objects that are recreated with each request tocoi::Container::resolve
.coi::ContainerBuilder
- a builder for the above container to simplify construction and guarantee immutability after construction.
How this crate works
For any trait you wish to abstract over, have it inherit the Inject
trait. For structs, impl
Inject
for that struct, e.g.
# use Inject;
;
Then, in order to register the injectable item with the coi::ContainerBuilder
, you also
need a struct that impls Provide<Output = T>
where T
is your trait or struct. Provide
exposes a provide
fn that takes &self
and &Container
. When manually implementing Provide
you must resolve all dependencies with container
. Here's an example below:
#
The "dependency"
above of course needs to be registered in order for the call
to resolve
to not error out:
#
In general, you usually won't want to write all of that. You would instead want to use the
procedural macro (see example below).
The detailed docs for that are at coi::Inject
(derive)
Example
#
Features
Compilation taking too long? Turn off features you're not using.
To not use the default, and e.g. only the "async" feature:
# Cargo.toml
[]
= { = "...", = false, = ["async"] }
- default:
derive-async
- Procedural macros are re-exported, async is available. derive
- Procedural macros are re-exported, but none of the code provides async support.async
- Procedural macros are not re-exported, so all generated code must be written manually. Async support is available.- None - Procedural macros are not re-exported and none of the code is async
Help
External traits
Want to inject a trait that's not marked Inject
? There's a very simple solution!
It works even if the intended trait is not part of your crate.
# use Inject;
// other.rs
// your_lib.rs
# /*
use coi::Inject;
use other::Trait;
# */
// Just inherit the intended trait and `Inject` on a trait in your crate,
// and make sure to also impl both traits for the intended provider.
Where are the factory registrations!?
If you're familiar with dependency injection in other languages, you might
be used to factory registration where you can provide a method/closure/lambda/etc.
during registration. Since the crate works off of the Provide
trait, you would
have to manually implement Provide
for your factory method. This would also
require you to manually retrieve your dependencies from the passed in Container
as shown in the docs above.
Why can't I derive Inject
when my struct contains a reference?
In order to store all of the resolved types, we have to use
std::any::Any
, which, unfortunately, has the restriction Any: 'static
.
This is because it's not yet known if there's a safe way to downcast to a
type with a reference (See the comments in this tracking issue).