Reputation: 2419
Consider the following Rust code:
#[derive(Debug, Clone, Copy)]
struct Struct;
trait Trait {}
impl Trait for Struct {}
fn f() {
// Why does this coerce to the target type (Result<Box<dyn Trait>, ()>)
let rbdt: Result<Box<dyn Trait>, ()> = Ok(Box::new(Struct));
// And this coerces to the target type...
let a = Box::new(Struct);
let rbdt: Result<Box<dyn Trait>, ()> = Ok(a);
// But this does not:
let a = Ok(Box::new(Struct));
let rbdt: Result<Box<dyn Trait>, ()> = a; // Error: mismatched types
}
Why do the first two assignments to rbdt
work correctly, coercing the value into the target type (Result<Box<dyn Trait>, ()>
), but the 3rd one does not? It seems to me that in all three cases the type of the RHS of the assignment is Result<Box<Struct>, ()
>, so it's perplexing that some forms work while other cause a mismatched types error.
What are the rules governing when types containing trait objects can be assigned to? Are these documented anywhere?
Upvotes: 4
Views: 519
Reputation: 13867
The underlying principle here is that the compiler will happily coerce a Box<Struct>
into a Box<dyn Trait>
by silently inserting an as
wherever it's needed. But it needs a place to actually be able to rewrite the code as, literally, boxed_struct as BoxedTrait
.
In the first example, this is possible:
let rbdt: Result<Box<dyn Trait>, ()> = Ok(Box::new(Struct) as Box<dyn Trait>);
And in the second:
let a = Box::new(Struct);
let rbdt: Result<Box<dyn Trait>, ()> = Ok(a as Box<dyn Trait>);
But in the third, there is nowhere for a Box<Struct>
-as-Box<Trait>
coercion to go. By the time you try to assign to rbdt
, the type of a
has already been determined to be Result<Box<Struct>, ()>
, and at this point it's too late to try re-inferring the type to be the more general Result<Box<Trait>, ()>
. The following is not a box-to-box coercion, so it isn't allowed.
let rbdt: Result<Box<dyn Trait>, ()> = a as Result<Box<dyn Trait>, ()>;
Box<Struct>
-as-Box<Trait>
is fine; Type<Box<Struct>>
-as-Type<Box<Trait>>
, for any Type
, is not.
Upvotes: 6
Reputation: 2202
I think this is less about coercion, and more about inference.
In first two cases it can immediately be inferred that T
in Result<Box<T>>
is something other than Struct
, while in the third Box<Struct>
is already a concrete type which then conflicts with T = dyn Trait
requirement introduced by type annotation on rdbt
.
Additionally I want to note that it is generally not possible to coerce a value that already exists to a type of a different, especially larger, size, which Box<Struct>
and Box<dyn Trait>
are due to the latter being a fat pointer.
Upvotes: 1