teloc icon indicating copy to clipboard operation
teloc copied to clipboard

Unable to register and resolve a singleton from the service container using Arc<dyn ...>

Open elertan opened this issue 2 years ago • 2 comments

I'm trying to register and resolve a singleton from the service container using Arc<dyn ...>, but cannot figure out as to why this does not work.

At first I tried to register the service using add_singleton_c by registering it as an Arc<MyServiceImpl> directly but that the conversion to Arc<dyn MyService> impossible to implement using From<_> due to the orphan rule. Even though this works on Box<_> just fine.

Then I tried to introduce a Wrapper type which wraps the Arc like so Wrapper(Arc<MyServiceImpl>). I thought this was necessary to implement the conversion, but currently that results in getting the error: the trait ResolveContainer<'_, Wrapper, _> is not implemented for SingletonContainer<Wrapper>.

Even though I think I implemented all the necessary traits on the types for it to implement ResolveContainer. I have created a small POC which should showcase the issue:

use std::sync::Arc;
use async_trait::async_trait;
use teloc::{Dependency, DependencyClone, Resolver, ServiceProvider};

#[async_trait]
pub trait MyService {
    async fn ping(&self);
}

#[async_trait]
impl MyService for MyServiceImpl {
    async fn ping(&self) {
        println!("Pong!");
    }
}

pub struct MyServiceImpl {}

#[teloc::inject]
impl MyServiceImpl {
    fn di_init() -> Self {
        Self {}
    }
}

// Unable to implement due to orphan rule...
// impl From<Arc<ExchangeImpl>> for Arc<dyn Exchange> {
//     fn from(value: Arc<ExchangeImpl>) -> Self {
//         value.clone()
//     }
// }
// or even...
// impl From<&Arc<ExchangeImpl>> for Arc<dyn Exchange> {
//     fn from(value: &Arc<ExchangeImpl>) -> Self {
//         value.clone()
//     }
// }

pub struct Wrapper(Arc<MyServiceImpl>);

#[teloc::inject]
impl Wrapper {
    fn di_init() -> Self {
        Self(Arc::new(MyServiceImpl {}))
    }
}

impl Clone for Wrapper {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

impl DependencyClone for Wrapper {}

impl From<Wrapper> for Arc<dyn MyService> {
    fn from(value: Wrapper) -> Self {
        value.0.clone()
    }
}

impl From<Box<MyServiceImpl>> for Box<dyn MyService> {
    fn from(value: Box<MyServiceImpl>) -> Self {
        value
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // This won't work since you cannot implement `From<Arc<ExchangeImpl>>` for `Arc<dyn Exchange>`
    // as it is not defined in this crate or marked as #[fundamental] like `Box<_>` is.
    // let sp = ServiceProvider::new().add_singleton_c::<Arc<dyn Exchange>, Arc<ExchangeImpl>>();
    let sp = ServiceProvider::new().add_singleton_c::<Arc<dyn MyService>, Wrapper>();
    let service: Arc<dyn MyService> = sp.resolve();

    // However using a box like this, works fine.
    // let sp = ServiceProvider::new().add_transient_c::<Box<dyn MyService>, Box<MyServiceImpl>>();
    // let exchange: Box<dyn MyService> = sp.resolve();

    service.ping().await;

    Ok(())
}

elertan avatar Sep 22 '23 10:09 elertan

I have found a solution when working with a wrapper type, we would need to implement:

impl<'a, T, Deps> ResolveContainer<'a, T, Deps> for SingletonContainer<T>
    where
        T: Dependency<Deps> + 'a,
        T: DependencyClone,
{
    fn resolve_container<F: Fn() -> Deps>(ct: &'a Self, get_deps: F) -> T {
        ct.get().get_or_init(|| T::init(get_deps())).clone()
    }
}

elertan avatar Sep 22 '23 12:09 elertan

@p0lunin Hi! Do you consider to review this changes? It looks like quite valuable improvement.

oriontvv avatar Oct 16 '23 08:10 oriontvv