Boxes and Dispatch and Impls, Oh My!

I have the following code.

use std::ops::Deref;

#[derive(Debug)]
struct Foo;

// HealthReporter (not a marker)
trait A {
    type Aa;
}

impl A for Foo {
    type Aa = ();
}

// impl A for smart pointers to anything impl A
impl<T, I> A for T where
    T: Deref<Target = I>,
    I: A + ?Sized,
{
    type Aa = I::Aa;
}

// HealthAware (marker)
trait B {}

impl<T: A + ?Sized> B for T {}

fn baz<T: B>(b: T) {}

fn main() {
    let foo: Box<dyn A<Aa = ()>> = Box::new(Foo);
    let bar: Box<dyn B> = Box::new(Foo);
    
    baz(foo);
    baz(bar); // fails
}

Also available on the playground.

I would like for Box<dyn B> to satisfy the trait bounds :B, but doing so results in:

error[E0277]: the trait bound `dyn B: Deref` is not satisfied
  --> src/main.rs:35:9
   |
35 |     baz(bar);
   |     --- ^^^ the trait `Deref` is not implemented for `dyn B`, which is required by `Box<dyn B>: B`
   |     |
   |     required by a bound introduced by this call
   |
note: required for `dyn B` to implement `A`
  --> src/main.rs:16:12
   |
16 | impl<T, I> A for T where
   |            ^     ^
17 |     T: Deref<Target = I>,
   |        ----------------- unsatisfied trait bound introduced here
   = note: 1 redundant requirement hidden
   = note: required for `Box<dyn B>` to implement `A`
note: required for `Box<dyn B>` to implement `B`
  --> src/main.rs:26:21
   |
26 | impl<T: A + ?Sized> B for T {}
   |         -           ^     ^
   |         |
   |         unsatisfied trait bound introduced here
note: required by a bound in `baz`
  --> src/main.rs:28:11
   |
28 | fn baz<T: B>(b: T) {}
   |           ^ required by this bound in `baz`

Am I thinking about this wrong? Is there some piece I haven't implemented? Does this require a fundamental capability that Rust doesn't have (...something about coercion...)?

Implement it for Box directly. Trying to do it with a blanket implementation will lead to pain.

I'm trying to do it with a blanket to allow for Box, Arc, Rc, etc., and also for Arc<Box<dyn B>> and similar

baz wants an implementor of B, but you have given it Box<dyn B>, which does not implement B. You've blanket implemented B for all A implementors, but this does not imply that every dyn B has an A implementation.

If you want it to be the case that all B implementations imply A implementations, so that dyn B can assume A is implemented, then declare that:

trait B: A {}

At this point B fits the “extension trait” pattern, though, so perhaps you’re looking for something else.

Yes, I understand what you're trying to do, but I recommend you give it up. You can do this:

impl<T: A + ?Sized> A for Box<T> { ... }
impl<T: A + ?Sized> A for Arc<T> { ... }
impl<T: A + ?Sized> A for Rc<T> { ... }

This will also give you Arc<Box<dyn B>>.

6 Likes

Doing so (new playground) yields

error[E0277]: the trait bound `dyn B: A` is not satisfied

Is this the root of the issue?


EDIT: I had forgotten that I had made A not actually a supertrait of B at some point. Looks like it should work if I fix that. I'll also test if my smart-pointer-generic solution works if I fix the first issue. Thanks for your help @alice

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.