How to accept type when been additionned to traits

Hello,

I have a type where I add auto trait requirements. When I want to use them in type with only the trait I have an error. It looks like a previous question but I'm not sure ...

There is:

trait Foo {}
type FooBox = Box<dyn Foo + Send + Sync>;

fn main() {
    let boxed_foos: Vec<FooBox> = vec![];
    let foos: Vec<Box<dyn Foo>> = boxed_foos;
}

The error:

➜  demo git:(master) βœ— cargo check
    Checking demo v0.1.0 (/home/bastiensevajol/Projets/demo)
error[E0308]: mismatched types
 --> src/main.rs:6:35
  |
6 |     let foos: Vec<Box<dyn Foo>> = boxed_foos;
  |               -----------------   ^^^^^^^^^^ expected trait `Foo`, found trait `Foo + Send + Sync`
  |               |
  |               expected due to this
  |
  = note: expected struct `Vec<Box<dyn Foo>>`
             found struct `Vec<Box<(dyn Foo + Send + Sync + 'static)>>`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `demo` (bin "demo") due to 1 previous error

How can I deal with that ?

dyn upcasting coercion is not implemented yet. It's trickier than one would think.

Edit

Above is wrong. That's not the problem. See below answer. Also dyn upcasting coercion is allowed when dealing with auto traits like Send and Sync.

For more information about type coercion, see The Rust Reference. It explains among other things that Box<dyn Foo + Send + Sync> can be coerced into Box<dyn Foo> but not other type constructors like Vec.

You have to coerce the Box<dyn Foo + Send + Sync> to Box<dyn Foo>, and that coercion cannot happen while they're nested in the Vec<_>.

let foos: Vec<Box<dyn Foo>> = boxed_foos.into_iter().map(|bx| bx as _).collect();

(dyn Foo + Send + Sync is not a subtype of dyn Foo, which is why a coercion is required.)

I was going to clarify that this doesn’t have runtime cost, but in this Compiler Explorer it generates quite a lot of assembly.

But interestingly, adding -C panic=abort to the compiler flags reduces it to a noop (this code is just moving it from the input to the return value with some SIMD tricks).

convert:
    mov     rax, rdi
    movups  xmm0, xmmword ptr [rsi]
    mov     rcx, qword ptr [rsi + 16]
    movups  xmmword ptr [rdi], xmm0
    mov     qword ptr [rdi + 16], rcx
    ret

Is there an operation in the code that could lead to a panic!, or is it just a missed optimization opportunity?

(My best guess is that the complexity comes from the closure |bx| bx as _, which the compiler thinks could panic and thus has to drop everything. panic=abort means none of that drop glue needs to be generated.)

Both :grin:! collect::<Vec<_>> notionally allocates.

It looks like the in-place collection is trait specialization based, and probably it just doesn't apply to Map. (But I didn't read the code in depth.)

First take, before I found the code

I don't know if the closure is introducing another potential panic from the POV of the compiler,[1] or if the presence of the closure/cast/map is just interfering with the "into_iter and collect" optimization that tries to reuse the Vec.[2]

Sticking map(identity) in the middle also results in a lot of drop glue, so I suspect the latter, but that's just a guess.


  1. and that's why there's all the drop glue β†©οΈŽ

  2. and the notional implementation of collect is why there's all the drop glue β†©οΈŽ

(Or, it's implemented for Map, but with drop-on-panic awareness.)

This is enough to get rid of the glue (local panic=abort emulation).

    std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
        boxed_foos.into_iter().map(|x| x as _).collect()
    })).unwrap_or_else(|_| std::process::abort())
1 Like

Interesting ! I will read the reference book about this. Thanks.

And, how can we do the opposite ?

trait Foo {}
type FooBox = Box<dyn Foo + Send + Sync>;
fn main() {
    let foos: Vec<Box<dyn Foo>> = vec![];
    let boxed_foos: Vec<FooBox> = foos;
}
error[E0308]: mismatched types
 --> src/main.rs:6:35
  |
6 |     let boxed_foos: Vec<FooBox> = foos;
  |                     -----------   ^^^^ expected trait `Foo + Send + Sync`, found trait `Foo`
  |                     |
  |                     expected due to this
  |
  = note: expected struct `Vec<Box<(dyn Foo + Send + Sync + 'static)>>`
             found struct `Vec<Box<dyn Foo>>`

Using as seems not appropriate here: an 'as' expression can only be used to convert between primitive types or to coerce to a specific trait object.

It seems impossible, right ? As dyn Foo do not cover Sync and Sync

Correct, it's generally not possible. The erased type may not implement the missing traits.

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.