RBF06
RBF06

Reputation: 2419

What are the rules for coercing values to trait objects?

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

Answers (2)

BallpointBen
BallpointBen

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

HJVT
HJVT

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

Related Questions