简体   繁体   中英

Allow trait object creation with user-defined type?

In Rust, references as well as Box<T> , Rc<T> , and Arc<T> allow the creation of a trait object (eg, from Box<Type> to Box<dyn Trait> ). But is there a way to allow the same conversion with a user-defined generic "smart pointer" type?

For example, MyBox<T> is a thin wrapper around Box<T> but the code below results in a compilation error:

use std::io::Write;

pub fn main() {
    let std_box: Box<Vec<u8>> = Box::new(Vec::new());
    let std_dyn: Box<dyn Write> = std_box;
    // ^ this conversion is allowed.

    let my_box: MyBox<Vec<u8>> = MyBox { t: Box::new(Vec::new()) };
    let my_dyn: MyBox<dyn Write> = my_box;
    // ^ this conversion is not allowed.
}

struct MyBox<T: ?Sized> {
    t: Box<T>,
}
error[E0308]: mismatched types
 --> traits/src/trait_objs.rs:7:36
  |
7 |     let my_dyn: MyBox<dyn Write> = my_box;
  |                 ----------------   ^^^^^^ expected trait object `dyn std::io::Write`, found struct `Vec`
  |                 |
  |                 expected due to this
  |
  = note: expected struct `MyBox<dyn std::io::Write>`
             found struct `MyBox<Vec<u8>>`

Unfortunately, it looks like the answer is "you don't" for Rust Stable. Rust is very conservative in which implicit coercions it allows. In particular, from the docs , we see the rule that's kicking in for Box .

Coercion is allowed between the following types:

...

  • TyCtor( T ) to TyCtor( U ), where TyCtor( T ) is one of

    • &T
    • &mut T
    • *const T
    • *mut T
    • Box<T>

and where U can be obtained from T by unsized coercion .

where the relevant unsized coercion rule is

  • T to dyn U , when T implements U + Sized , and U is object safe .

There's not much room for special casing there, at least not in the current version of Rust.

However, if you're willing to dip into Nightly-only features, then you get the exciting CoerceUnsized trait , whose intended use case is... smart pointers to things which would coerce like a Box would.

#![feature(unsize, coerce_unsized)]

use std::ops::CoerceUnsized;
use std::marker::Unsize;

impl<T, U> CoerceUnsized<MyBox<U>> for MyBox<T> where T: Unsize<U>, U: ?Sized {}

This tells Rust we can coerce MyBox<T> to MyBox<U> if T is "basically the same" as U , for an appropriate definition of "basically the same". No functions are required on the implementation; the trait impl simply needs to exist.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM