Cannot satisfy `_: Runnable<dyn A, Arc<Mutex<Option<Box<dyn A>>>>>`

I designed a trait that can run things that are Lockable, that is, can be locked to produce Option<T>, and I implemented it for Arc<Mutex<Option<Box<T>>>>

use std::sync::{LockResult, PoisonError, MutexGuard, Arc, Mutex};

pub type LockableArc<T: ?Sized> = Arc<Mutex<Option<T>>>;

pub struct MutexGuardOptionRef<'a, T: ?Sized> {
    pub mutex_guard: MutexGuard<'a, Option<Box<T>>>,
}

pub trait LockableOption<T: ?Sized>: Clone + Send {
    fn lock(&self) -> LockResult<MutexGuardOptionRef<T>>;
}

pub trait Runnable<R: ?Sized, T: LockableOption<R> + Clone + Send + ?Sized> {
    fn run(s: T) -> Result<(), ()>;
}

impl<T: ?Sized + Send> LockableOption<T> for LockableArc<Box<T>> {
    fn lock(&self) -> LockResult<MutexGuardOptionRef<T>> {
        unimplemented!()
    }
}

pub trait A: Send{}

pub struct S{}

impl A for S{}

impl<R: A, T: LockableOption<R> + Clone + Send + 'static> Runnable<R, T>
    for S
{
    fn run(arc_rtsp: T) -> Result<(), ()> {
        Ok(())
    }
}

fn main() {
    let r: LockableArc<Box<dyn A>> = Arc::new(Mutex::new(Some(Box::new(S{}))));
    Runnable::run(r.clone());
    //Runnable::<dyn A, LockableArc<Box<dyn A>>>::run(r.clone());
}

Playground

Error:

error[E0283]: type annotations needed
  --> src/main.rs:39:5
   |
14 |     fn run(s: T) -> Result<(), ()>;
   |     ------------------------------- required by `Runnable::run`
...
39 |     Runnable::run(r.clone());
   |     ^^^^^^^^^^^^^ cannot infer type
   |
   = note: cannot satisfy `_: Runnable<dyn A, Arc<Mutex<Option<Box<dyn A>>>>>`

I don't understand this error. First because I don't know what '_ is. It looks like it's the type passed to run as the only argument. So I tried to force the type:

Runnable::<dyn A, LockableArc<Box<dyn A>>>::run(r.clone());

but I get the same error. What is happening?

-impl<R: A, T: LockableOption<R> + Clone + Send + 'static> Runnable<R, T>
+impl<R: A + ?Sized, T: LockableOption<R> + Clone + Send + 'static> Runnable<R, T>
     for S
 {
     fn run(arc_rtsp: T) -> Result<(), ()> {
         Ok(())
     }
 }
 
 fn main() {
     let r: LockableArc<Box<dyn A>> = Arc::new(Mutex::new(Some(Box::new(S{}))));
     //Runnable::run(r.clone());
-    Runnable::<dyn A, LockableArc<Box<dyn A>>>:run(r.clone());
+    <S as Runnable::<dyn A, LockableArc<Box<dyn A>>>>::run(r.clone());
 }

Playground.

You need the ?Sized un-bound to cover dyn A, and the compiler needed help figuring out exactly what implementation of Runnable you were talking about (or it was being conservative re: future implementers).

There were two backticks (`...`) around everything, so it wasn't '_, it was

_: Runnable<...>

And it's trying to say, "I'm looking for some unknown type (_) which implements this bound (: Runnable<...>), but I couldn't figure it out." To which you say, here I'll help you out, it's

<ThisSpecificType as ThisTrait>::trait_stuff(...)

As it turns out, from that point it can be simplified to

-    <S as Runnable::<dyn A, LockableArc<Box<dyn A>>>>::run(r.clone());
+    <S as Runnable::<_, _>>::run(r.clone());

Playground.

I understood. However,

<S as Runnable::<dyn A, LockableArc<Box<dyn A>>>>::run(r.clone());

I have to cast S to a Runnable. What if r does not contain an S but contains another struct that also dyn A? It looks strange to use a specific struct like S as Runnable, since I'd like to not even know which concrete type was being used in r, it could be anything, decided at runtime

(Although you use the as keyword, it's not a cast; it's just a way to fully qualify which implementation of Runnable you're calling.)

You mean, you want something like this?

fn g(thing: LockableArc<Box<dyn A>>) -> Result<(), ()> {
    Runnable::<_,_>::run(thing.clone())
}

It's ambiguous because the way you have your traits set up, there could be multiple implementations that qualify. So you would need to know which implementation, e.g.

fn g<X>(thing: LockableArc<Box<dyn A>>) -> Result<(), ()>
where
    X: Runnable<dyn A, LockableArc<Box<dyn A>>>
{
    <X as Runnable<_, _>>::run(thing.clone())
}

Updated example. I've added code to show that

  • You can have multiple implementers of Runnable<dyn A, LockableArc<Box<dyn A>>>
  • And because which implementing type is hidden behind dyn, you need to specify it...
    • g::<S>(r.clone());
    • g::<S2>(r2.clone());

But also to show that, also because of dyn

  • The implementation used doesn't even have to match the underlying type
    • g::<S>(r2);
    • g::<S2>(r);

If you need them to match and to carry that information around in their API, you need some other arrangement. Note that the above is related to why associated types of traits must be specified as part of the dyn Trait type.

Now I see why the error happens. I made Runnable too generic. However, there should be only one run function per trait. My original idea was a Runnable that expects an object that simply implements LockableOption<T>:

pub trait Runnable<T: ?Sized> {
    fn run(s: Box<dyn LockableOption<T>>) -> Result<(), ()>;
}

However when fillings things for this to work, I get that Box<dyn LockableOption<T>> cannot be cloned. See the example:

I think this is because for a Box to be cloned, it has to clone its inside, which is different for each dyn. I tend to keep forgetting that Box is not like a pointer.

What do you think? The idea is simply a run that accepts something that implements LockableOption<T>. Then it simply runs this T, locking it at every iteration.

I suspect there's too much being generic and indirection generally. They are probably there for reasons that aren't clear from the example, but it certainly makes it hard to reason about, I keep having to bounce back up to the definitions. So for the sake of exploring, I took your playground and iteratively simplified it:

pub type LockableArc<T: ?Sized> = Arc<Mutex<Option<T>>>;

pub struct MutexGuardOptionRef<'a, T: ?Sized> {
    pub mutex_guard: MutexGuard<'a, Option<Box<T>>>,
}

I just replaced the alias everywhere. The latter as presented is just a newtype; maybe you need to implement things on it, but to simply things, I replaced every MutextGuardOptionRef<_> with a MutexGuard<'Option<Box<_>>> as well.

After those replacements we have this:

pub trait LockableOption<T: ?Sized>: Send {
    fn lock(&self) -> LockResult<MutexGuard<Option<Box<T>>>>;
}

impl<T: ?Sized + Send> LockableOption<T> for Arc<Mutex<Option<Box<T>>>> {
    fn lock(&self) -> LockResult<MutexGuard<Option<Box<T>>>> {
        unimplemented!()
    }
}

There's only one sensible implementation though, which is (**self).lock(). Or if you renamed the method to avoid the name conflict, just self.lock(). At this point the trait seems superfluous as well, so I removed it.

Here's what was left. There's still a problem with finding the correct implementation. You say there should only be one run function per trait, I take this to mean that for any type T: A, there should only be one implementation of Runnable. This implies we should actually be writing the implementation like this:

-impl Runnable<dyn A> for S
+impl<T: A> Runnable<dyn A> for T
 {
     fn run(s: Arc<Mutex<Option<Box<dyn A>>>>) -> Result<(), ()> {

And this again highlights the same issue as before, of course we need to say which implementation, given this blanket rule. But if T always must be tied to dyn A like this, why is the dyn A a type parameter to Runnable? We explicitly don't want this ability to mix and match that the type parameter is allowing.

If we remove the type parameter:

-pub trait Runnable<T: ?Sized> {
-    fn run(s: Arc<Mutex<Option<Box<T>>>>) -> Result<(), ()>;
+pub trait Runnable {
+    fn run(s: Arc<Mutex<Option<Box<Self>>>>) -> Result<(), ()>;
 }
 // ...
-impl<T: A> Runnable<dyn A> for T
+impl Runnable for dyn A

Everything compiles without fully qualified paths. If you don't need different run behavior per dyn Trait, you could even do this:

-impl Runnable for dyn A {
-    fn run(s: Arc<Mutex<Option<Box<dyn A>>>>) -> Result<(), ()> {
+impl<T: ?Sized + Send + 'static> Runnable for T {
+    fn run(s: Arc<Mutex<Option<Box<T>>>>) -> Result<(), ()> {

OK, I think the takeaway is "see if removing the type parameter of Runnable can satisfy your use case." I may well have removed too much indirection for your use case to work. Maybe you could start here and build back up to it.

Here's another variation where Runnable::run takes self, if that's a better fit.


You asked a specific question about boxes and clones, which disappeared somewhere among the simplifications. In your link, it's complaining about

doesn't satisfy `dyn LockableOption<(dyn A + 'static)>: Clone`

And if you try to make Clone a supertrait of LockableOption, you get

9  | pub trait LockableOption<T: ?Sized>: Clone + Send  {
   |           --------------             ^^^^^ ...because it requires `Self: Sized`
   |           |
   |           this trait cannot be made into an object...

I still don't understand why this needs to be a trait, given the signatures, but let's say it does need to. First, see if you need the type parameter too, it may cause similar problems as above. Next, I haven't used it and it's not available in the playground, but There's A Crate For That™. I definitely trust the crate owner.

But also, instead of using dyn LockableOption, you could be generic over some U: LockableOption. Then you won't need to clone a Box<dyn LockableOption> at all.

the reason I needed LockableOption is because I'm not sure and I don't want to be sure about which object will be passed to run. In this case it was Arc<Mutex<Option<Box<dyn A>>>> but it could be Arc<Mutex<dyn A>> or Arc<Mutex<Option<Arc<dyn A>>>>, etc. It could also be a type that holds an Arc<Mutex<Option<Box<dyn A>>>>. In fact, anything that could be both cloned and locked. Cloned because it will be shared against threads (that's why most of the types I showed are Arc) and locked because something shared between threads should be locked.

That's why I created LockableOption. It unlocks into Option<Box<T>>:

/// 
pub struct MutexGuardOptionRef<'a, T: ?Sized> {
    pub mutex_guard: MutexGuard<'a, Option<Box<T>>>,
}

/// Unlocks into something that guards Option<T>. At least was the idea, but I had to put a Box to accomodate the not Sized cases like dyn A
pub trait LockableOption<T: ?Sized>: Send {
    fn lock(&self) -> LockResult<MutexGuardOptionRef<T>>;
}

The Box here is simply so it can hold dyn A, because T can be not Sized

If I do simply

pub trait LockableOption<T: ?Sized>: Send {
    fn lock(&self) -> LockResult<MutexGuard<T>>;
}

then this unlocks into T, which is not always the case. For example, Arc<Mutex<Option<Box<T>>>> cannot unlock into Box<T>. What would be done if Option is None?

So I guess all of this justifies the types I created and unfortunately I cannot get rid of them.

now that you mentioned, one of your modifications kinda makes sense:

pub trait LockableOption<'a, T: ?Sized>: Send {
    fn lock(&self) -> LockResult<MutexGuard<'a, Option<T>>;
}

but I remembered why I didn't make it like this. On my original MutexGuardOptionRef, I implemented the inner method that returns Option<&T>, which is very useful because it guards an Option<Box<T>>, which is annoying to use. That's where you mentioned (**self).lock() I guess

It can't, you would have to create an Option out of thin air. Would be nice if you didn't need the Option though.

How's this?

Edit: Do you even need the Clone bound? Maybe only the callers of run care about that.

-    fn run<T: 'static + Clone + LockableOption<Self>>(s: T) -> Result<(), ()> {
-        let s = s.clone();
+    fn run<T: 'static + LockableOption<Self>>(s: T) -> Result<(), ()> {
         std::thread::spawn(move ||{let _ss = s;});
         Ok(())
     }

It can't, you would have to create an Option out of thin air. Would be nice if you didn't need the Option though.

Indeed, this case will not be possible. I though I could make it such that it always returns a Some. I guess this can be changed if I do some things in MutexGuardOptionRef. Anyways:


impl Runnable for dyn A {
    fn run<T: 'static + Clone + LockableOption<Self>>(s: T) -> Result<(), ()> {
        let s = s.clone();
        std::thread::spawn(move ||{let _ss = s;});
        Ok(())
    }
}

I think you did this because I said

However, there should be only one run function per trait.

Sorry, it was my fault, I communicated it wrong. It's almost what you did, but it should be one run function per trait implementation:

impl Runnable for S {
    fn run<T: 'static + Clone + LockableOption<Self>>(s: T) -> Result<(), ()> {
        let s = s.clone();
        std::thread::spawn(move ||{let _ss = s;});
        Ok(())
    }
}

where S: A, and another implementation for SS: dyn A, etc.

So when I do Runnable::run(something), it runs the something's run implementation. For example, something: Arc<Mutex<Option<Box<dyn A>>>> would call S::run, since S: A.

I tried doing some modifications: Rust Playground but since Runnable::run has no &self, dyn A cannot be made into an object.

I think the problem relies mainly in Runnable::run having no self parameters. Some parameters are allowed like self: Arc<Self> or self: Box<Self>, but not complicated ones like Arc<Mutex<Option<Box<Self>>>>>.

I don't have a problem with &self, I just didn't do it because it was not possible. It'd be even better to call something.run()

Well... there's still some ambiguity or inconsistency here. I did only implement it for dyn A. And you can add that implementation for S. But as illustrated, if you want to use the implementation for S, you carry around a Box<S>, not a Box<dyn A>.

But from your most recent playground, I think you don't want to run "something's run when you do Runnable::run(something)", you want to run "something's run when you do Runnable::run(dyn A)". That is, you want different implementations per T: A even though the only implementer of A in the argument is dyn A.

You're not going to be able to carry around a <...<dyn A>> and call the implementation for S (or S2, etc) without doing something like:

  • Hardcoding <S as Runnable>::...
  • Using a struct that carries around a PhantomData<S> and a dyn A to recover the S
  • In the implementation for dyn A, try to downcast (directly or via methods as discussed in other threads) to S, S2, etc., and call the matching implementation

Rephrased: When you use dyn A, you're saying "Only the properties of trait A matter, not the implementing type such as S. Please forget about S and friends! Just use some generated vtable and hide all the things like S behind this other concrete type, dyn A. I'll use that instead of S, S2, etc."

But here the implementing type does matter, so you either need to give up on dyn, hard-code the identify of S with fully qualified paths, carry the identify of S through along side dyn (largely negating the point), or use the escape hatch of downcasting to recover S.

Downcasting seems like the most likely approach that preserves everything you say you want (though I don't know how ergonomic or performant it will be). On the other hand, "I want one dynamic type but I want distinct behavior per implementing type" keeps coming up in your threads, so I really wonder if an enum with some dispatching boilerplate (i.e. giving up on dyn) would suit this use case better.

I don't want a Runnable for dyn A. I want to be able to hold LockableArc<dyn A> and let the runtime magic take care of which run to call, depending on the actual concrete type inside dyn A.

Let's suppose:

struct S{}
struct SS{}
//impl Runnable for S
//impl Runnable for SS
impl A for S{}
impl A for SS{}

struct R{}
//impl Runnable for R
impl B for R{}
struct RR{}
//impl Runnable for RR
impl B for RR{}
fn main() {
    let r: LockableArc<Box<dyn A>> = Arc::new(Mutex::new(Some(Box::new(S{}))));
    //This runs S's `run` implementation
    let _ = Runnable::run(r.clone());
    let r: LockableArc<Box<dyn A>> = Arc::new(Mutex::new(Some(Box::new(SS{}))));
    //This runs SS's `run` implementation
    let _ = Runnable::run(r.clone());

    let r: LockableArc<Box<dyn B>> = Arc::new(Mutex::new(Some(Box::new(R{}))));
    //This runs R's `run` implementation
    let _ = Runnable::run(r.clone());
    let r: LockableArc<Box<dyn B>> = Arc::new(Mutex::new(Some(Box::new(RR{}))));
    //This runs RR's `run` implementation
    let _ = Runnable::run(r.clone());
}

Actually the bigger idea would be for the types to be interchangeable:

let r: Box<dyn LockableOption<Box<dyn A>>> = Box::new(Arc::new(Mutex::new(Some(Box::new(S{})))));
let _ = Runnable::run(r.clone());

What I mean by this? Both LockableArc<T> = Arc<Mutex<Option<T>>> and LockableArc2<T> = Arc<Mutex<Option<Arc<T>>>> and LockableArc3<T> = Arc<Mutex<Option<Result<T>>> would fit into r and could be swapped at runtime (I mean their boxes, since they have different sizes). But I guess this is impossible.

It all comes down to LockableOption being impossible to make an object of of it, because it does not have &self so a vtable cannot be constructed. That's odd because C++ pointers have no problem in having vtables when they have static methods.

I'm trying to think in many ways to redesign this but it's hard.

All of this is great:

pub type LockableArc<T> = Arc<Mutex<Option<T>>>;

pub struct MutexGuardOptionRef<'a, T: ?Sized> {
    pub mutex_guard: MutexGuard<'a, Option<Box<T>>>,
}

pub trait LockableOption<T: ?Sized>: Send {
    fn lock(&self) -> LockResult<MutexGuardOptionRef<T>>;
}

impl<T: ?Sized + Send> LockableOption<T> for LockableArc<Box<T>> {
    fn lock(&self) -> LockResult<MutexGuardOptionRef<T>> {
        (**self).lock()
            .map(|mutex_guard| MutexGuardOptionRef { mutex_guard })
            .map_err(|e| {
                let mutex_guard = e.into_inner();
                let mgor = MutexGuardOptionRef { mutex_guard };
                PoisonError::new(mgor)
            })
    }
}

and I think it's the best model for what I want to do, but the Runnable part seems to be forcing things a little

Throughout your example that follows, you have thrown the concrete-but-not-dyn type out (S, SS, R, RR) and left only the trait object (dyn A, dyn B). You have opted out of the compiler knowing, tracking, or acting differently in any way based on the underlying type. There is no differing behavior based on something occluded from the method API, no monomorphizing without generics, and dyn Trait is a concrete type, not a generic one.

The only runtime magic to get the non-dyn type back out of the dyn is downcasting (or hacks that approximate it like the specific-type-returning methods discussed elsewhere). There may be crates to make this somewhat easier, but there is no compiler magic to say "do this casting and downcasting for me through an Arc, Mutex, Option, and Box." (Well OK, there is one for just a Box.)

Moreover, once you get your downcast, you'll be holding a &S that's lifetime limited to the method. If you want to be able to send these things to (non-scoped) threads, send them first and then downcast them on the receiving end.

I still cannot see why doing this

let r: LockableArc<Box<dyn A>> = Arc::new(Mutex::new(Some(Box::new(S{}))));
let _ = Runnable::run(r.clone());

would erase which run function to run.

For example:


trait Runnable {
    fn run(&self);
}

struct A{}

struct B{}

impl Runnable for A {
    fn run(&self) {
        println!("run A");
    }
}

impl Runnable for B {
    fn run(&self) {
        println!("run B");
    }
}

fn main() {
    let r: Box<dyn Runnable> = Box::new(A{});
    r.run();
    let r: Box<dyn Runnable> = Box::new(B{});
    r.run();
}

why here the information is not erased but it would on the example

    let r: LockableArc<Box<dyn A>> = Arc::new(Mutex::new(Some(Box::new(S{}))));
    //This runs S's `run` implementation
    let _ = Runnable::run(r.clone());
    let r: LockableArc<Box<dyn A>> = Arc::new(Mutex::new(Some(Box::new(SS{}))));
    //This runs SS's `run` implementation
    let _ = Runnable::run(r.clone());

?

I think I understand what I asked

pub trait Runnable {
    fn run<T: 'static + Clone + LockableOption<Self>>(s: T) -> Result<(), ()>;
}

this is parametrized compile-time. So if I call it with a LockableArc<Box<dyn A>> it will choose the implementation for LockableArc<Box<dyn A>> that implements LockableOption.

To make it work on runtime like I want, it should be something like this

pub trait Runnable {
    fn run(s: Box<dyn LockableOption<???>>) -> Result<(), ()>;
}

it should expect a dyn to a LockableOption which has not been possible

I wrote the below reply to your first post, and then thought about my reply in the context of your second post. After doing that, I think you probably understand everything below already, which is why you keep bringing up dyn LockableOption, and we're just struggling to communicate clearly. (But I'm leaving the reply there anyway as I'm still not 100% sure of that.)

I'll try and find time to take another look later... I don't know if it's possible though, through two layers of dyn (without the same issues encountered before). You need some way to tie them together.


In the long example, each dyn Runnable contains a vtable that points to the implementor-of-Runnable's implementation (along with a pointer to the implementing type's data). dyn Runnable also implements Runnable. When you call r.run(), it's calling dyn Runnable's run in both cases. It takes the data pointer and the vtable entry and in turn calls the implementing type's run. That is to say, it performs dynamic dispatch.

It's still type erased, it's just utilizing the vtable.

In the bottom code block, the two calls to run are calling the same function, with different values. But the value is a LockableArc<Box<dyn A>>, not a receiver like &self. It doesn't do dynamic dispatch. If you want to do anything different in run itself, you need to downcast. And yes, you can utilize the vtable too (indirectly, by calling methods on the dyn A). But to do either of these things, you're going to have to get ahold of a reference to dyn A (or really, in this case, Box<dyn A>). And to do that, you have to lock the Mutex, match the Option, etc.

I think the way of doing this would be to receive Box<dyn LockableOption<T>> on run, but given that we can't Clone LockableOption, this is not possible.

Another easy option would be to implement LockableOption for Arc<Mutex<Option<Box<T>>>> and for other things as well, and have


pub trait LockableOption<T: ?Sized>: Send {
    fn lock(&self) -> LockResult<MutexGuardOptionRef<T>>;
    fn run(&self) -> Result<(),()>
}

but this is not what I wanted.

I'm starting to think it's not possible, but it seems like a very simple problem that would have a simple solution.

You can do this, but if you want to dynamic dispatch on &dyn Outer to different implementations, you need an implementation per inner type -- not just for <<<dyn Inner>>> -- and those inner types would have to be part of the outer type in some way.

I think I understand what you mean, see

impl Runnable for dyn A {
    fn run<T: 'static + Clone + LockableOption<Self>>(s: T) -> Result<(), ()> {
        //Here we receive LockableOption<dyn A>
        //and I should downcast to `S` or `SS`
        //but unfortunately I cannot do this as this code would not
        //know how many implementations there are (`S, SS, SSS, ...`)
        //If there were a way to dynamically dispatch here, it would be great
        let s = s.clone();
        std::thread::spawn(move ||{let _ss = s;});
        Ok(())
    }
}

inspired by your last example, there should be a way here to dinamically dispatch to S: A or S: AA. However I see no way of using the inner type without locking, and we can't lock, we must let each implementation lock itself. To call S::something we must lock first, which is already wrong.

So, we can't downcast because we don't know all implementations, and we can't lock either

I made a radical change. Instead of a global Runnable, by doing a Runnable per trait, I've come up with Rust Playground

use std::sync::{Arc, LockResult, Mutex, MutexGuard, PoisonError};

pub type LockableArc<T> = Arc<Mutex<Option<T>>>;

pub struct MutexGuardOptionRef<'a, T: ?Sized> {
    pub mutex_guard: MutexGuard<'a, Option<Box<T>>>,
}

pub trait LockableOption<T: ?Sized>: Send + Sync {
    fn lock(&self) -> LockResult<MutexGuardOptionRef<T>>;
}

impl<T: ?Sized + Send> LockableOption<T> for LockableArc<Box<T>> {
    fn lock(&self) -> LockResult<MutexGuardOptionRef<T>> {
        unimplemented!()
    }
}

pub trait Decoder<T>: Send {
}

pub struct FfmpegDecoder<T> {
    x: T,
}

impl<T: 'static + Send> Decoder<T> for FfmpegDecoder<T> {
}

trait DecoderRunnable<T> {
    fn run(s: Arc<dyn LockableOption<dyn Decoder<T>>>);
}

impl<T: 'static + Send> DecoderRunnable<T> for FfmpegDecoder<T> {
    fn run(s_lockable: Arc<dyn LockableOption<dyn Decoder<T>>>) {
        unimplemented!()
    }
}

fn main() {
    let r: LockableArc<Box<dyn Decoder<u8>>> = Arc::new(Mutex::new(Some(Box::new(FfmpegDecoder{x: 0u8}))));
    let rr: Arc<dyn LockableOption<dyn Decoder<u8>>> = Arc::new(r);
    //I still have to pick the implementation here, which is not ok
    <FfmpegDecoder<u8> as DecoderRunnable::<u8>>::run(rr.clone());
}

this almost works except for the fact that I need to specify which implementation of DecoderRunnable is going to be used. But at least now there's no Inner vs Outer trait, there's only Inner now. Do you know how to modify this so I don't need to specify the implementation?