1个不稳定版本
0.2.0 | 2023年9月15日 |
---|
#2014 在 进程宏 中
用于 chassis
45KB
1K SLoC
Chassis
受Dagger 2启发的Rust编译时依赖注入器
目标
- 无需对类进行注释(支持第三方类)
- 无需使用
std::sync::Arc
- 零开销:速度快如手写代码
- 不使用运行时类型信息(由
std::any::Any
引用提供) - 目前依赖于编译器优化,直到min_specialization
稳定。
- 不使用运行时类型信息(由
- (在编译时检测错误,如缺少依赖项或循环依赖项)
- 等待编译时反射或至少
min_specialization
、const_type_id
和const_cmp_type_id
的稳定
- 等待编译时反射或至少
使用
将chassis
添加到您的crate依赖项中
[dependencies]
chassis = "^0.2.0"
结构体将成为可以提供依赖项(带有函数)的模块,并且自身也可以有依赖项。注意:目前仅支持关联函数!
#[derive(Default)]
pub struct Module;
#[chassis::module]
impl Module {
pub fn provide_something(dep1: Dependency1, dep2: Dependency2) -> Dependency3 {
Dependency3::new(dep1, dep2)
}
// ...
}
特性将成为组件。对于每个特性,将创建一个实现的组件。生成的实现将具有Impl
后缀,例如ComponentImpl
。同时还会创建一个Component::new
函数。
#[chassis::injector(modules = [Module])]
pub trait Component {
fn resolve_main_class(&self) -> MainClass;
}
示例
use std::rc::Rc;
// define your business logic
/// printer trait
pub trait Printer {
fn print(&self, input: &str);
}
/// a printer implementation
pub struct StdoutPrinter;
impl Printer for StdoutPrinter {
fn print(&self, input: &str) {
println!("{}", input);
}
}
/// greeter for messages
pub struct Greeter {
message: String,
printer: Rc<dyn Printer>,
}
impl Greeter {
/// constructor with dependencies
pub fn new(message: String, printer: Rc<dyn Printer>) -> Self {
Self { message, printer }
}
/// your business logic
pub fn say_hello(&self) {
self.printer.print(&self.message);
}
}
/// module that is parsed to create the dependency injection code
#[derive(Default)]
pub struct DemoModule;
// use strong types when in need to distinguish
pub struct Message(String);
/// Define how to create your dependencies
#[chassis::module]
impl DemoModule {
pub fn provide_printer() -> Rc<dyn Printer> {
Rc::new(StdoutPrinter)
}
pub fn provide_message() -> Message {
Message("Hello World".to_string())
}
pub fn provide_greeter(
message: Message,
printer: Rc<dyn Printer>
) -> Greeter {
Greeter::new(message.0, printer)
}
}
/// Define which dependencies you need.
///
/// A struct `DemoComponentImpl` will be created for
/// you which implements `DemoComponent`.
#[chassis::injector(modules = [DemoModule])]
pub trait DemoComponent {
/// request the to create injection code for our main class `Greeter`
fn resolve_greeter(&self) -> Greeter;
}
fn main() {
// use generated component implementation
let injector = <dyn DemoComponent>::new()
.expect("DI container should be consistent");
// Resolve main dependency
// Note: it can not fail at runtime!
let greeter = injector.resolve_greeter();
// enjoy!
greeter.say_hello();
}
单例
通常,对于每个所需的依赖项,都会调用模块上的提供者函数。这会导致类型被多次创建。这可能并非本意。解决方案是使用 singleton
属性。在这种情况下,提供方法将在组件构建时(调用 ComponentImpl::new
)仅调用一次。要求类型实现 Clone
特性。建议对单例使用共享引用类型,如 Rc
或 Arc
,以确保只创建一个实例。
示例
#[chassis::module]
impl Module {
#[chassis(singleton)]
pub fn provide_printer() -> Rc<dyn Printer> {
Rc::new(StdoutPrinter)
}
}
限制
- 类型中的生命周期不受支持(除了
'static
) - 泛型处理不正确
- 在模块中请求已注册的非引用类型的引用(当
MyType
由模块提供时,请求&MyType
) - 延迟请求(请求提供者而不是具体类型)
- 可选请求(仅在存在时获取)
- 多个提供者(对插件很有用)
- 失败的模块函数(在模块中返回
Result
)
许可证
根据您的选择,许可协议为以下之一
- Apache License,版本 2.0(《LICENSE-APACHE》或https://apache.ac.cn/licenses/LICENSE-2.0》)
- MIT 许可协议(《LICENSE-MIT》或http://opensource.org/licenses/MIT》)
。
贡献
除非您明确说明,否则您根据 Apache-2.0 许可协议有意提交的任何贡献,都将根据上述方式双许可,不附加任何额外条款或条件。
依赖项
~0.6–1.1MB
~25K SLoC