Lockjaw
Lockjaw is a fully static, compile-time dependency injection framework for Rust inspired by Dagger. It is also what you get when jabbed by a rusty dagger.
Features:
- Compile time dependency resolution
- Lockjaw makes sure all dependencies are fulfilled at compile time. The code will fail to compile if a dependency is missing, there are duplicated bindings for the same type, or if the dependency graph has cycles. There will be no runtime errors which are harder to detect.
- Relatively readable diagnostic messages.
- When a dependency is missing Lockjaw tries to tell you why it is even in the dependency graph, and where the dependency cycle is.
- Cross-crate injection
- Lockjaw is designed to be used across crates. Clients are able to inject bindings provided by libraries if they also use Lockjaw.
- Minimal generated code surface
- While procedural macros are utilized heavily by Lockjaw, it avoids directly modifying the code
the attributes macros are placed on. Only a few generated methods are visible to the user.
This is especially important since most Rust IDEs today does not understand the output of
procedural macros, a few extra type hints on
letexpressions is enough to make autocomplete functional.
- While procedural macros are utilized heavily by Lockjaw, it avoids directly modifying the code
the attributes macros are placed on. Only a few generated methods are visible to the user.
This is especially important since most Rust IDEs today does not understand the output of
procedural macros, a few extra type hints on
- Optional binding, Multibinding, and generated components for plugin systems
- Lockjaw allows inversion of control between library crates and their user. A library is able to define hooks for clients that depends on the library to inject. This is especially useful to test multiple clients using a library in isolation.
See user guide for more information.
Example:
use *;
use Add;
prologue!;
// Allow GreetCounter to be created in the dependency graph. These bindings are available anywhere.
// Declare a module so we can do special bindings. These bindings are only available if the
// component installs the module, so different bindings can be used based on the situation.
// Components stitch modules and injectables together into a dependency graph, and can create
// objects in the graph. The component installs modules listed in `modules`
// called at the binary to perform validation and code generation
epilogue!;
A more complicated game example can be found at https://github.com/azureblaze/lockjaw/tree/main/example_game
Comparison with Dagger
Lockjaw Aims for feature parity with Dagger and uses very similar APIs. If you have used Dagger before, lockjaw should feel familiar.
@Inject→#[inject]constructor injection in#[injectable]@Provides→#[provides]bind method return values@Binds→#[binds]bind trait to implementation.@Singleton/@Scope→scope=componentshared instance.@Named→#[qualified]Provider<T>→Provider<T>create multiple instances at run time.Lazy<T>→Lazy<T>create and cache instance only when used.- Subcomponents
→
#[subcomponent]Dynamically creatable sub-scopes with additional bindings - Multibindings
→
#[into_vec]/#[into_map]Collect same bindings to a Vec/HashMap, useful for plugin systems. @BindsOptionalOf→#[binds_option_of]Allow some bindings to be missing- Factories
→
#[facotry]create objects with both injected fields and runtime fields. - Hilt
→
#[define_component]/#[entry_point/install_inAutomatic module collection from build dependency.
Disclaimer
This is not an officially supported Google product.
Lockjaw is currently in early development and all APIs are subjected to changes. Some feature are also implemented in a hacky way. Use at your own risk.