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日

#22 in 配置

Download history 178/week @ 2024-05-04 106/week @ 2024-05-11 210/week @ 2024-05-18 121/week @ 2024-05-25 119/week @ 2024-06-01 289/week @ 2024-06-08 231/week @ 2024-06-15 230/week @ 2024-06-22 229/week @ 2024-06-29 145/week @ 2024-07-06 189/week @ 2024-07-13 261/week @ 2024-07-20 506/week @ 2024-07-27 177/week @ 2024-08-03 498/week @ 2024-08-10 293/week @ 2024-08-17

1,555 每月下载量

MIT/Apache

47KB
857

黄麻

Rust的运行时依赖注入库

Crates.io CI Dependencies

这个包仍然处于早期阶段,需要大量的工作,但它在kamu-cli中被积极使用——这是一个根据Onion/Clean Architecture组织的相当大的项目。我们在遇到更复杂的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 功能添加到第三方代码中
  • 关注 运行时 注入
    • 利用类型系统和零成本抽象很棒,但很难做对 - 这个项目开始是因为我们急需一个实用的东西
    • 有些情况涉及对象的动态注册(例如在处理 HTTP 请求期间添加身份验证令牌),这进一步复杂化了编译时的 DI
    • 我们在可以容忍一些开销的情况下使用 DI 来集成粗粒度组件
    • 我们通过提供运行时图验证来补偿安全性
  • 关注 构造函数注入
    • 基于字段/属性/访问器的注入会使系统复杂化,并且在我们经验中作用很小
  • 实现权 交给实现者
    • 类型实现者(而不是类型使用者)通常对类型的最佳生命周期和并发特性有最深入的了解,因此实现者应控制默认值

待办事项

  • 支持 stable Rust
  • 支持提供自身接口列表的构建器(默认绑定)
  • 改进图验证
  • 使 Scope 成为 Builder 的外部,以便可以覆盖它们
  • 允许动态注册(无需克隆整个目录)
  • 考虑使用特质将 ArcOptionVec 映射到依赖规范,而不是依赖于宏魔法
  • 添加 trybuild 测试(见 https://youtu.be/geovSK3wMB8?t=956
  • 支持泛型类型
  • 用泛型 add<B: Into<Builder>> 替换 add_*
  • new() 中的按引用传递值
  • 向错误添加低开销的解析堆栈(例如在 unwinding 时填充)
  • 额外的作用域
    • 调用
    • 线程
    • 任务
    • 目录?
  • 线程安全
  • 懒值
  • 外部定义的类型
  • 自定义构建器
  • 错误处理
  • 文档测试
  • 改进目录流畅接口(或宏?)
  • 过程宏错误处理
  • 构建一个未注册的类型
  • 支持PImpl习语,其中 Arc<dyn Iface> 可以被一个可移动对象隐藏
    • 这进一步隐藏了生命周期管理对消费者的可见性
    • 允许实现泛型方法以改进 dyn Trait 的可用性(例如,接受 impl AsRef<str> 参数而不是 &str

依赖关系

~0.4–0.8MB
~19K SLoC