trait FooTrait {}
struct FooStruct;
impl FooTrait for FooStruct {}
fn main() {
let maybe_struct: Option<dyn FooStruct> = None;
// Does not compile
let maybe_trait: Option<Box<dyn FooTrait>> = maybe_struct.map(Box::new);
// Compiles fine
let maybe_trait: Option<Box<dyn FooTrait>> = match maybe_struct {
Some(s) => Some(Box::new(s)),
None => None,
};
}
error[E0404]: expected trait, found struct `FooStruct`
--> src/main.rs:9:34
|
9 | let maybe_struct: Option<dyn FooStruct> = None;
| ^^^^^^^^^ not a trait
Rustc 1.23.0. Why doesn't the first approach compile? Am I missing something obvious, or... huh?
We create a trait object by specifying some sort of pointer, such as a & reference or a Box<T> smart pointer, then the dyn keyword, and then specifying the relevant trait.
Object Safety A trait is object safe if it has the following qualities (defined in RFC 255): All supertraits must also be object safe. Sized must not be a supertrait. In other words, it must not require Self: Sized . It must not have any associated constants.
Box::new
only works with sized types; that is, it takes a value of a sized type T
and returns Box<T>
. In certain places a Box<T>
can be coerced into a Box<U>
(if T: Unsize<U>
).
Such coercion does not happen in .map(Box::new)
, but does in Some(Box::new(s))
; the latter is basically the same as Some(Box::new(s) as Box<FooTrait>)
.
You could create (in nightly) your own box constructor that returns boxes of unsized types like this:
#![feature(unsize)]
fn box_new_unsized<T, U>(v: T) -> Box<U>
where
T: ::std::marker::Unsize<U>,
U: ?Sized,
{
Box::<T>::new(v)
}
and use it like .map(box_new_unsized)
. See Playground.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With