Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I downcast from Box<Any> to a trait object type?

Tags:

rust

downcast

pub struct WidgetWrap {
    // ...
    widget: RefCell<Box<Any>>,
}

At some point I want to cast Box<Any> to Box<WidgetTrait>

let mut cell = widget.borrow_mut();
let w = cell.downcast_mut::<Box<WidgetTrait>>();

This gives me an error of this kind:

error: instantiating a type parameter with an incompatible type
`Box<WidgetTrait>`, which does not fulfill `'static` [E0144]

What does this really mean?

I've looked at How to fix: value may contain references; add `'static` bound to `T` and did try adding + 'static everywhere.

pub struct WidgetWrap {
    // ...
    widget: RefCell<Box<Any + 'static>>,
}
let mut cell = widget.borrow_mut();
let w = cell.downcast_mut::<Box<WidgetTrait + 'static>>();

It fixes the compile errors, but fails when I try to unwrap the downcasted box as shown above. And yes, the content of the box is an object that implements WidgetTrait.

Obviously, I am coding in Rust at a level that I don't quite understand, but maybe someone can help me get a better grip on the concepts involved in the above task.

like image 224
porgarmingduod Avatar asked Aug 11 '14 15:08

porgarmingduod


People also ask

How do you create a trait object?

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.

What is downcasting in Rust?

rust. Downcasting is Rust's method of converting a trait into a concrete type. It is done using the Any trait, which allows "dynamic typing of any 'static type through runtime reflection" (docs).

What is a trait object?

A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.


1 Answers

(I shall ignore the 'static part as it’s comparatively irrelevant for the parts I’m explaining.)

Box<Trait> for a given trait Trait is stored as two pieces of data: a pointer to the actual data in memory and a pointer to the vtable for its type’s implementation of Trait.

From that, you may see that you can only have one level of traityness—if you have a Box<WidgetTrait> and you box it again as Box<Any>, you would only be able to get it out as a Box<WidgetTrait> object. Similarly, if you take a type Widget that implements WidgetTrait and box it in a Box<Any>, you can only get it out as a Widget object, not as a Box<WidgetTrait> object.

Such is the nature of the type IDs being used internally: unlike in a dynamic or VM-based language, the type system is purely a compile-time construct; there is no such thing as the type system at runtime.

The solution, if you really need a solution along these lines (you probably don’t; sticking with just a Box<WidgetTrait> is probably the best way) is to have a trait which also implements what Any does. This is not the simplest thing at present, but can be done. Teepee’s Header trait is an example of how this can work; a Box<Header> object will have the header-transforming methods as well as Any’s .downcast_ref() and so forth.

like image 82
Chris Morgan Avatar answered Jan 16 '23 22:01

Chris Morgan