💉
【Rust】axum + shakuでDIコンテナを状態管理する際のCloneトレイト問題とArcによる解決
問題
axumでshakuのモジュールをStateとして使用する際、以下のようなエラーに遭遇することがある
the trait bound `AppModule: Clone` is not satisfied
the trait `Clone` is not implemented for `AppModule`
原因
この問題は以下の2つの要因から発生する:
- axumのRouterは状態(State)にCloneトレイトの実装を要求する
- shakuのmodule!マクロで生成されたモジュールには直接#[derive(Clone)]を付けることができない
解決方法
解決策として、shakuのモジュールをArcでラップする
registry
// 例)
module! {
pub AppModule {
components = [DoSomethingRepositoryImpl],
providers = []
}
}
impl AppModule {
pub fn new(pool: ConnectionPool) -> Arc<Self> {
Arc::new(
AppModule::builder()
.with_component_parameters::<DoSomethingRepositoryImpl>(
DoSomethingRepositoryImplParameters { db: pool }
)
.build()
)
}
}
pub type AppRegistry = Arc<AppModule>;
これが有効な理由:
- ArcはCloneトレイトを実装している
- ArcのCloneは参照カウントのインクリメントのみを行い、データの実際のコピーは行わない
- ArcはSend + Syncも実装しているため、axumの非同期マルチスレッド環境での要件も満たす
実装例
router
// ルーターの実装例)
pub fn build_example_routers() -> Router<AppRegistry> {
let routers = Router::new()
.route("/example", get(do_something));
Router::new().nest("/any", routers)
}
注意点
- RcもCloneを実装しているが、Send + Syncを実装していないため、axumでは使用できない
- shakuのモジュールに直接#[derive(Clone)]を付けることはできないため、Arcによるラッピングが必要
まとめ
axumでshakuのモジュールを状態として使用する場合、ArcでラップすることでCloneトレイトの要件を満たし、かつスレッドセーフな実装を実現できる。
Discussion