(最后更新于: )
阅读时间: 5 分钟

Rust IOC 容器搭建

利用 Rust 语言特性,纯静态零消耗的 IOC 容器

Table of Contents

架构设计

本设计灵感来源于 TD-Sky(受 toetoe55 启发),特此致谢!

第一步:定义依赖的 trait 和实现

// path/to/domain/foo_service.rs:
pub trait FooService {
    fn foo_bar(&self);
}

声明结构体 FooServiceState 并实现 FooService,使用 dep-inj 库。

以下是宏展开后的等效代码:
// path/to/service/foo_service.rs:
use path::to::domain::FooService;

#[derive(dep_inj::DepInj)]
#[target(FooServiceImpl)]
pub struct FooServiceState {
    bar: u64
}

impl FooServiceState {
    pub fn new(bar: u64) -> Self {
        Self {
            bar
        }
    }
}

/// 该结构体的方法不调用其他结构体的方法,
/// 意味着它不依赖任何其他结构体。
/// 因此实际上无需为其派生 DepInj。
/// 以下代码即可实现:
/// ```rust
/// pub struct FooServiceImpl { bar: u64 }
/// impl FooService for FooServiceImpl {
///     fn foo_bar(&self) {
///         println!("{}", self.bar);
///     }
/// }
/// ```
/// 此处使用 DepInj 仅作示例。
/// 若需访问 state 中的字段,必须显式指定
impl<Deps> FooService for FooServiceImpl<Deps>
where
    Deps: AsRef<FooServiceState>
{
    fn foo_bar(&self) {
        /// &self 自动通过 as_ref 转为 FooServiceState,故可访问其字段
        println!("{}", self.bar);
    }
}

// DepInj 宏展开后生成的代码:
#[repr(transparent)]
/// __Deps__ 指定了类型解引用所需实现的 trait
pub struct FooServiceImpl<__Deps__: ?Sized> {
    /// To pass compile
    _marker: ::core::marker::PhantomData<FooServiceState>,
    deps: __Deps__,
}
impl<__Deps__> Copy for FooServiceImpl<__Deps__> where __Deps__: Copy {}

/// 为 Impl 实现 Deref 到 State
impl<__Deps__: ?Sized> ::core::ops::Deref for FooServiceImpl<__Deps__>
where
    __Deps__: AsRef<FooServiceState>,
{
    type Target = FooServiceState;
    #[inline]
    fn deref(&self) -> &Self::Target {
        self.deps.as_ref()
    }
}
impl<__Deps__: ?Sized> ::core::ops::DerefMut for FooServiceImpl<__Deps__>
where
    __Deps__: AsRef<FooServiceState> + AsMut<FooServiceState>,
{
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.deps.as_mut()
    }
}
impl<__Deps__> From<FooServiceImpl<__Deps__>> for FooServiceState
where
    __Deps__: Into<FooServiceState>,
{
    fn from(value: FooServiceImpl<__Deps__>) -> Self {
        value.prj().into()
    }
}
impl<__Deps__> Clone for FooServiceImpl<__Deps__>
where
    __Deps__: Clone,
{
    fn clone(&self) -> Self {
        Self {
            _marker: ::core::marker::PhantomData,
            deps: self.deps.clone(),
        }
    }
}
//...
// Some factory methods
// inj method is to transfer a borrow of generic type __Deps__
// to Self(FooServiceImpl<__Deps__> here).
// pri method is to transfer a borrow of self to a borrow of generic type __Deps__.
impl<__Deps__: ?Sized> FooServiceImpl<__Deps__> {
    #[inline]
    pub fn inj_ref(deps: &__Deps__) -> &Self {
        unsafe { &*(deps as *const __Deps__ as *const Self) }
    }
    #[inline]
    pub fn prj_ref(&self) -> &__Deps__ {
        unsafe { &*(self as *const Self as *const __Deps__) }
    }
    #[inline]
    pub fn inj_ref_mut(deps: &mut __Deps__) -> &mut Self {
        unsafe { &mut *(deps as *mut __Deps__ as *mut Self) }
    }
    #[inline]
    pub fn prj_ref_mut(&mut self) -> &mut __Deps__ {
        unsafe { &mut *(self as *mut Self as *mut __Deps__) }
    }
    //...
}
impl<__Deps__> FooServiceImpl<__Deps__> {
    #[inline]
    pub fn inj(deps: __Deps__) -> Self {
        Self {
            _marker: ::core::marker::PhantomData,
            deps,
        }
    }
    #[inline]
    pub fn prj(self) -> __Deps__ {
        self.deps
    }
}

第二步:编写容器(Container)

容器用于持有所有状态,并在 boilerplate 中通过宏生成的 inj 方法为容器实现所有 trait。
使用 derive_more::AsRef 为容器添加 AsRef<FooService> 能力,可以在 &Contaner 使用 inj_ref 之后,使用 FooService 的方法:

代码:
// path/to/container.rs
// The Service need to use Other dependencies only need to depend on Container
#[derive(derive_more::AsRef)]
pub struct Container {
    #[as_ref]
    pub foo_service: FooServiceState,
}

impl Container {
    // Initialize method for Container.
    pub fn new(config: &AppConfig) -> Self {
        let foo_service = FooServiceState::new(config.num);
        Self {
            foo_service
        }
    }
}

// .../boilerplate.rs
use path::to::Container;
use path::to::domain::FooService;
use path::to::foo_service::FooServiceImpl;
use path::to::bar_service::BarServiceImpl;

// Boilerplate for provide FooService implement for Container
impl FooService for Container {
    fn foo_bar(&self) {
        // Use inj_ref() generated by dep-inj macro,
        // to get &FooServiceImpl from Container
        FooServiceImpl::inj_ref(self).foo_bar()
    }
}

impl BarService for Container {
    fn bar_foo(&self) {
        BarServiceImpl::inj_ref(self).bar_foo()
    }
}

第三步:实现依赖其他服务的结构体

然后,我们编写 Barservice,他依赖于 Fooservice
dep_inj_target 宏用于在无字段结构体上生成 dep_inj

代码:
// path/to/bar_service.rs
// Use of the Dependency Injection System!
use path::to::domain::FooService;

#[dep_inj_target::dep_inj_target]
pub struct BarServiceImpl;

impl<Deps> BarServiceImpl<Deps>
where
    Deps: FooService
{
    pub fn bar_foo(&self) {
        // 2. Use deref mechanism to auto use method under trait `FooService`
        self.prj_ref().foo_bar()
        // Or `(self.prj_ref() as &dyn FooService).foo_bar()`
    }
}

最终调用示例

最终,我们可以通过 Entry 去调用 BarServiceImpl 中的 bar_foo 方法,在 bar_foor 中,又调用了 FooServiceImplfoo_bar 方法!

代码:
// .../entry.rs
use path::to::Container;

pub struct Entry {
    container: Container
}

impl Entry {
    fn run_bar_foo(&self) {
        // The container implements the `FooService` bound, so it can satisfy
        // `BarServiceImpl`'s call on `bar_foo()`.
        // in `bar_foo`, it converts to a reference to `Deps`,
        // which can call `foo_bar` from `FooService`
        // because the Container implements the `FooService` with call of `FooServiceImpl`'s `inj_ref` method
        // and AsRef<FooServiceState>(generated by derive(AsRef)), the foo_bar method can be called successfully!
        self.container.bar_foo();
        // Or (self.container as dyn BarService).bar_foo()
    }
}

核心机制总结

  1. 依赖推导: 容器通过 AsRef 自动推导依赖关系,满足 trait 约束。

  2. 零成本抽象: 使用宏生成的 inj_ref 方法,无需运行时开销即可转换类型引用。

  3. 编译期约束: 所有依赖的 Send/Sync 约束会在编译期递归检查,确保线程安全。

  4. 灵活扩展: 新增服务只需实现对应 trait 并注册到容器,无需修改已有代码。

注意事项

💡

在此架构中,deps 会递归地接受所有依赖字段的 Send 和 Sync 约束的交集。 这意味着,如果你想为 Arc<Self> 类型实现函数,一旦某个依赖在容器中是 !Sync 的,整个容器将无法通过编译检查。

例如:

impl<Deps> A for AImpl<Deps>
where
    Deps: AsRef<AImpl> + B + Send
{
    fn aa(self: Arc<Self>){
        // your logic here
    }
}

如果 BImplState 中有任何字段是 !Sync 的,整个代码将无法通过编译,因为 Arc<Self> 要求整个 Deps 必须满足 Send 和 Sync trait 约束,但某个字段缺少 Sync 约束。


zooeywm
嗨! 我是 zooeywm

一名对新鲜事物永远怀有热情的咸鱼程序员~

欢迎光临我的赛博小窝! (:3[▓▓]

Arch Linux | Hyprland | neovim | Rust | 能用键盘不用鼠标

|


Content licenced under CC BY-NC-ND 4.0