#injection #di #ioc #dependencies

nightly build kamikaze_di

Rust 中依赖注入的探索

1 个不稳定版本

0.1.0 2019年6月4日

#389构建工具


kamikaze_di_derive 中使用

MIT 许可证

37KB
525

Kamikaze DI 构建状态

这是一个 Rust 的依赖注入容器。它受到了其他语言容器库的启发。

我主要想知道人们对它的看法,以及是否有人想使用这样的东西。

它的工作原理

let config = container.resolve::<Config>(); // simple resolve via the Resolver trait
let config: Config = cotantiner.inject();  // using Injector and Inject/InjectAsRc

// deriving the Inject trait teaches the container how to create it
#[derive(Inject, Clone)]
struct DabatabaseConnection {
    config: Config,
    ...
}

有关更多信息,请参阅示例和文档。

安装

获取基础 crate 和 derive crate。

[dependencies]
kamikaze_di = "0.1.0"
kamikaze_di_derive = "0.1.0"
# or use this for slightly better debug! logs in the derive crate
# log = "0.4.6"
# kamikaze_di_derive = { version = "0.1.0", features="logging" }

这需要 rust nightly

讨论

Rust 有两个重要概念:所有权和可变性。这两个概念都影响了我们 DI 容器的设计。

所有权

数据只能有一个所有者,所以在进行操作时,谁拥有什么

let db = container.resolve::<Database>();

db 对象来自容器内部。它必须在某个时候拥有它,现在当前作用域拥有了它。那么当我们再次解析 Database 时会发生什么呢?

工厂

一种方法是让容器充当工厂。虽然有时这是所希望的(并通过 .register_factory::<T>() 支持),但这绝对不是合理的默认设置,我们如何共享东西呢?

副本

如果我们返回前复制或克隆对象,那么我们可以共享东西。但有些东西可能永远不应该共享。

克隆不可克隆的?

其他语言没有这个问题,因为所有东西都存在于堆中,并且是引用计数的。听起来像是 Rc<>,不是吗。

使用 Rc

容器构建器上所有注册函数的类型签名类似于

    fn register<T>(&mut self, item: T) -> Result<()> where T: Clone

我们始终需要 Clone,一些类型可以接受这个。对于其他类型,你可以使用 Rc。

let database = ...;
builder.register(Rc::new(database));

Rc 也可以与特性对象一起使用

let database: MysqlConnection = ...;
builder.register::<Rc<Database>>(Rc::new(database));

为什么不使用 &T 呢?

我早期就决定使用 Clone/Rc,但我非常不确定这是否是正确的选择。

关于可变性怎么办?

如果你正在获取克隆对象,可变性是你的责任。如果你正在使用 Rc,情况就不同了:Rc::get_mut() 总是返回 None,因为容器总是会保持对其的引用。你需要使用内部可变性。

关于 Sync 呢?

这是一个非常好的问题。

基本上,我不想添加它。我只是想开始一场讨论,我不想维护一个我自己都不会使用的工具,而且我写的 Rust 代码还不够多,无法做到这一点。

自动推导

如果 AutoResolvable 特性在作用域内,容器将尝试自己找出如何创建依赖项。这通常会在运行时通过反射来完成,但 Rust 不支持这一点。

任何实现了 InjectInjectAsRc 的类型都可以以这种方式解决。当然,自己编写所有这些代码是非常繁琐的。所以为什么不直接推导呢?

// Just derive this trait
#[derive(Inject, Clone)]
struct YourStruct {
// ...
}

所有这些类型的依赖项都需要推导 InjectInjectAsRc 或在容器中注册。

错误

当类型无法解决时,你会得到相当不错的错误信息。以下是 unwrapping 错误时你会得到的内容。

could not resolve Jester::voice_box : Rc < VoiceBox > ...

这并不完美,错误信息没有使用类型的完整路径,但它可能足以确定出了什么问题。

恐慌

此项目仅在循环依赖的情况下应该恐慌,任何其他恐慌都是错误。

示例

repo 和文档中都有示例。

依赖项

~87KB