17个不稳定版本 (8个破坏性更改)
新功能 0.9.1 | 2024年8月15日 |
---|---|
0.9.0 | 2024年7月29日 |
0.8.1 | 2024年6月20日 |
0.8.0 | 2023年11月28日 |
0.2.0 | 2021年7月17日 |
53 在 #ioc 中
每月下载量:1,301
用于 dill
29KB
520 行
这个crate还处于早期阶段,需要大量工作,但它已经在 kamu-cli 中活跃使用——这是一个根据 Onion/Clean Architecture 组织的相当大的项目。我们将继续改进这个crate,并在遇到更复杂的DI场景时不断完善。
示例
/////////////////////////////////////////
// Define interfaces in traits
trait A: Send + Sync {
fn test(&self) -> String;
}
// Implement traits to define components
#[component]
struct AImpl {
// Auto-inject dependencies (also supports by-value)
b: Arc<dyn B>,
}
impl A for AImpl {
fn test(&self) -> String {
format!("aimpl::{}", self.b.test())
}
}
/////////////////////////////////////////
trait B: Send + Sync {
fn test(&self) -> String;
}
#[component]
struct BImpl;
impl B for BImpl {
fn test(&self) -> String {
"bimpl".to_owned()
}
}
/////////////////////////////////////////
// Register interfaces and bind them to implementations
let cat = CatalogBuilder::new()
.add::<AImpl>()
.bind::<dyn A, AImpl>()
.add::<BImpl>()
.bind::<dyn B, BImpl>()
.build();
// Get objects and have their deps satisfied automatically
let inst = cat.get::<OneOf<dyn A>>().unwrap();
assert_eq!(inst.test(), "aimpl::bimpl");
功能
- 注入规范
OneOf
- 期望给定接口的单个实现AllOf
- 返回给定接口上所有实现的集合Maybe<Spec>
- 如果内部Spec
无法解决,则返回None
- 组件作用域
Transient
(默认)- 每次调用都会创建一个新的实例Singleton
- 在首次使用时创建实例,然后在整个调用过程中重复使用
#[component]
宏可以派生Builder
- 当直接用于
struct
或带有Impl::new()
函数的impl
块时 - 可以作为
Arc<T>
、T: Clone
、&T
注入 Option<T>
被解释为Maybe<OneOf<T>>
规范Vec<T>
被解释为AllOf<T>
规范- 支持在
Builder
中使用自定义参数绑定 - 支持通过
#[interface]
属性进行默认接口绑定 - 支持通过
#[meta(...)]
属性进行元数据关联
- 当直接用于
- 支持预构建/按值添加
- 支持对
Clone
类型的按值注入 Catalog
可以自我注入Catalog
的链式调用允许动态添加值(例如在tower
中像中间件链一样)
设计原则
- 非侵入式
- 编写与DI(依赖注入)友好的代码应尽可能接近编写常规类型
- DI应该是一个可添加的功能 - 我们应该能够禁用它并且所有代码都能编译(即允许DI可选库)
- 可外部化
- 应该能够将DI功能添加到第三方代码中
- 专注于 运行时 注入
- 利用类型系统和零成本抽象是很好的,但很难做到正确 - 这个项目开始是因为我们需要快速得到一些实用的东西
- 有些情况涉及到对象的动态注册(例如在处理HTTP请求期间添加认证令牌),这进一步复杂化了编译时DI
- 我们在需要可接受一些开销的情况下使用DI来集成粗粒度组件
- 我们通过提供运行时图验证来补偿安全性
- 专注于 构造函数注入
- 基于字段/属性/访问器的注入会使系统复杂化,而且根据我们的经验,用处不大
- 将 实现控制权 放在实现者手中
- 类型实现者(而不是类型使用者)通常对类型应该有的最佳生命周期及其并发特性有最好的了解,因此实现者应该控制默认值
待办事项
- 支持
stable
Rust - 支持提供自己的接口列表的构建器(默认绑定)
- 改进图验证
- 将
Scope
使其独立于Builder
,以便可以覆盖它们 - 允许动态注册(无需克隆整个目录)
- 考虑使用特质将
Arc
、Option
、Vec
映射到依赖规范,而不是依赖于宏魔法 - 添加
trybuild
测试(见 https://youtu.be/geovSK3wMB8?t=956) - 支持泛型类型
- 用泛型
add<B: Into<Builder>>
替换add_*
- 在
new()
中按引用传递值 - 向错误添加低开销的解析堆栈(例如在展开时填充)
- 额外的作用域
- 调用
- 线程
- 任务
- 目录?
- 线程安全
- 懒值
- 外部定义的类型
- 自定义构建器
- 错误处理
- 文档测试
- 改进目录流畅的接口(或宏?)
- 过程宏错误处理
- 构建一个无需注册的类型
- 支持PImpl习语,其中
Arc<dyn Iface>
可以隐藏在可移动对象后面- 这甚至进一步将生命周期管理隐藏给消费者
- 允许实现泛型方法来提高
dyn Trait
的可用性(例如,接受impl AsRef<str>
参数而不是&str
)
依赖项
~260–710KB
~17K SLoC