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

前端 未结 2 1201
失恋的感觉
失恋的感觉 2020-11-28 16:10
pub struct WidgetWrap {
    // ...
    widget: RefCell>,
}

At some point I want to cast Box to B

相关标签:
2条回答
  • 2020-11-28 16:33

    The original code works as-is in at least Rust 1.25; presumably there was a bug or limitation in the compiler that has since been fixed:

    use std::{any::Any, cell::RefCell};
    
    trait WidgetTrait {}
    
    fn example(widget: RefCell<Box<dyn Any>>) {
        let mut cell = widget.borrow_mut();
        let _w = cell.downcast_mut::<Box<dyn WidgetTrait>>();
    }
    

    As pointed out by trentcl, that doesn't mean that this does what you want:

    This compiles, I admit, but it cannot succeed when the content of the box is an object that implements WidgetTrait, because it downcasts specifically to Box<WidgetTrait (+ 'static)>. You'd have to create a Box<Box<WidgetTrait + 'static>> for it to work. (Put another way, the type parameter of downcast_mut is the unboxed type T, not Box<T>.)

    use std::{any::Any, cell::RefCell};
    
    trait WidgetTrait {
        fn demo(&mut self);
    }
    
    fn example(widget: RefCell<Box<dyn Any>>) {
        let mut cell = widget.borrow_mut();
        match cell.downcast_mut::<Box<dyn WidgetTrait>>() {
            Some(w) => w.demo(),
            None => println!("Not here!"),
        }
    }
    
    struct Alpha(u8);
    impl WidgetTrait for Alpha {
        fn demo(&mut self) {
            self.0 += 1;
            dbg!(self.0);
        }
    }
    
    fn main() {
        let b: Box<dyn Any> = Box::new(Alpha(0));
        let r = RefCell::new(b);
        example(r);
    }
    
    Not here!
    

    Boxing it twice:

    fn main() {
        let b: Box<dyn WidgetTrait> = Box::new(Alpha(0));
        let b2: Box<dyn Any> = Box::new(b);
        let r = RefCell::new(b2);
        example(r);
    }
    
    [src/main.rs:19] self.0 = 1
    

    See also:

    • How to get a reference to a concrete type from a trait object?
    0 讨论(0)
  • 2020-11-28 16:44

    (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.

    0 讨论(0)
提交回复
热议问题