Impl for trait needs Arc<T>

Most of this is reasonably straightforward but my challenge is that I want to implement a trait, but I need self to be in an Arc for the implementation. So, in the code below it's the // I want Arc<Client> here so I can clone it is the important point.

I tried several approaches including implementing the trait for Arc<Client> but that doesn't work. I also had the idea of having Client implement an InnerTrait and then use that inside the Arc for things, but couldn't get that to work either.

Playground link (doesn't work of course but might be easier to work with than code here)

use std::sync::Arc;

struct Client;
struct Other;

impl Other {
    fn run_task(c: Arc<Client>, function) -> Other {
        // spawns a task that runs the function param
    }
}

trait Factory {
    fn func(&self, val: i32) -> Other;
}

// I could impl for Arc<Client> but that causes a problem later
impl Factory for Client {
    fn func(&self, val: i32) -> Other {
        // I want Arc<Client> here so I can clone it
        Other::run_task(self_that_is_in_Arc.clone(), move |c| c.do_stuff());
    }
}

struct X {
    my_factory: Arc<dyn Factory>
}

// I could have `where Arc<T>: Factory` but that causes a problem later
impl X {
    pub fn new(the_factory: Arc<dyn Factory>) -> Self {
        Self {
            my_factory: the_factory
        }
    }
}

pub fn main() {
    let first_client = Arc::new(Client::new());
    X::new(first_client.clone());
}

you can use Arc<Self> as the method receiver:

trait Factory {
    fn func(self: Arc<Self>, val: i32) -> Other;
}

note, self: Arc<Self> requires the caller transfer the ownership of Arc. self: &Arc<Self> also works, so you can make the clone only when needed.

note, using &Arc<Self> as receiver makes the trait not dyn-compatible. now that you cannot use dyn Factory, you'll have to use generics for struct X:

struct X<F> {
    my_factory: Arc<F>
}

// I could have `where Arc<T>: Factory` but that causes a problem later
impl<F> X<F> {
    pub fn new(the_factory: Arc<F>) -> Self where F: Factory {
        Self {
            my_factory: the_factory
        }
    }
}

using self: &Arc<Self> is rather uncommon, I feel like you have a deeper design problem, and Arc is just NOT the right solution. maybe you could share more details about the problem you are trying to solve?

1 Like

Arc<Self> is a dyn-compatible receiver.

Did you mean &Arc<Self>? That one is not dyn-compatible.

2 Likes

oopsie, nice catch. edited.

1 Like

Well... I want Arc because that Client needs to be used all over the place including in async tasks and threads, including into the thing I need to have a Factory for. That's where this comes from:

impl Factory for Client {
    fn func(&self, val: i32) -> Other {
        // I want Arc<Client> here so I can clone it
        Other::run_task(self_that_is_in_Arc.clone(), move |c| c.do_stuff());
    }
}

Other::run_task spawns a thread and I need to pass in either a function or equivalent into it that will be moved into that thread and run there. That's why I pass in an Arc which will be later called back into the closure I'm passing in (so self_that_is_in_Arc.clone() will be the c in the closure, and that might seem odd but there are lifetimes and stuff involved and this was possibly the only way to get it to work without moving into the realm of self referencing types, etc.).

Some of that internal part (the thread and such) is driven by a separate library I can't change, so...

I know I could use a type param on struct X but I'm specifically trying to avoid that because then it would force propagating that type all over the place. And this isn't isolated, so with other similar cases, that could get rather messy.

I'm thinking the solution is probably to introduce an inner type:

trait Inner {}

impl Inner for Client {}

impl Factory for Arc<Client> {
    fn func(&self, val: i32) -> Other {
        Other::run_task(self.clone(), move |c| c.do_stuff());
    }
}

struct X {
    my_factory: Arc<dyn Inner>
}

impl X {
    pub fn new<T>(the_factory: Arc<T>) -> Self
    where T: Inner, Arc<T>: Factory {
        Self {
            my_factory: the_factory
        }
    }
}

But I couldn't figure out how to tell the compiler that all Arc implemented Factory. Either I tried a blanket impl:

impl<T:Inner> Factory for T {...}

which didn't work because there are conflicting impls for that blanket and the one for Client.

or everything would compile except when inside impl X I tried to call func on Arc, it would say the trait bounds weren't met.

I don't quite follow, some clarification is needed, mainly, which part of the API is third party code, and which part is under your control?

is the signature of Other::run_task() defined by external code, or your own? if I read your problem correctly, the reason you need Arc<Self> is because Oterh::run_task() requires it?

what's the use case for the Factory trait? what other types also implement Factory? do they also need Arc<Self>? or its just Client?