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

Download history 260/week @ 2024-04-24 177/week @ 2024-05-01 92/week @ 2024-05-08 178/week @ 2024-05-15 162/week @ 2024-05-22 92/week @ 2024-05-29 180/week @ 2024-06-05 271/week @ 2024-06-12 284/week @ 2024-06-19 197/week @ 2024-06-26 212/week @ 2024-07-03 123/week @ 2024-07-10 281/week @ 2024-07-17 486/week @ 2024-07-24 230/week @ 2024-07-31 263/week @ 2024-08-07

每月下载量:1,301
用于 dill

MIT/Apache

29KB
520

dill

Rust的运行时依赖注入库

Crates.io CI Dependencies

这个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,以便可以覆盖它们
  • 允许动态注册(无需克隆整个目录)
  • 考虑使用特质将 ArcOptionVec 映射到依赖规范,而不是依赖于宏魔法
  • 添加 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