Downcasting and the Interior Mutability Pattern

Hi all,

I am trying to downcast from a 'dyn ValueTrait' into the Function struct, which implements the ValueTrait. When I do it with variables, it seems to work. But when I do it with the interior mutability pattern, it does not. My code looks as follows, and it always results in panic.

// function is a Rc<RefCell<dyn ValueTait>>
let f = match function.as_any().downcast_ref::<Rc<RefCell<Function>>>() {
                Some(func) => {
                    func
                },
                None => {
                    panic!("Could not downcast Value to Function")
                }
          };

I also did this:

impl ValueTrait for Rc<RefCell<dyn ValueTrait>> {

    fn to_string(&self)->String {
        format!("fn {}()",self.borrow().to_string())
    }

    fn type_name(&self)->String {
        self.borrow().type_name()
    }

        
    fn as_any(&self) -> &dyn Any{
        self
    }    
        
}

Is it possible to do this?? What am I not seeing?

All the best!

Can you make a playground link which shows the problem?

I had no idea I could do that... does it work?

Awesome, your playground link works!

Downcasting isn't transitive, so when you call as_any, you can only downcast back to Rc<RefCell<dyn ValueTrait>>. You'll have to get at the dyn ValueTrait inside the RefCell directly to downcat it separately. Something like:

rcvalue.borrow().as_any().downcast_ref::<Function>()

Thanks! it is working now... updated in the same link.

I fought this thing for a couple of days and, now that I manage to get it to work, I notice it reduced significantly reduced the performance of my code :roll_eyes:.... But, I learned. I will see what happened later.

Thanks a lot!

use std::any::Any;
use std::cell::RefCell;
use std::rc::Rc;


// These two are just placeholders
struct ScriptFn (f64);
struct RustFn (f64);

trait ValueTrait {
    fn to_string(&self)->String;
    fn type_name(&self)->String;
    fn as_any(&self)->&dyn Any;
}

#[allow(dead_code)]
enum Function {
    Script(ScriptFn),
    Native(RustFn)
}



// Trait for Function
impl ValueTrait for Function {

    fn to_string(&self)->String {
        "fn Func()".to_string()
    }

    fn type_name(&self)->String {
        "Function".to_string()
    }

        
    fn as_any(&self) -> &dyn Any {
        self
    }    
        
}

// Trait for Values contained in this pattern
impl ValueTrait for Rc<RefCell<dyn ValueTrait>> {

    fn to_string(&self)->String {
        format!("fn {}()",self.borrow().to_string())
    }

    fn type_name(&self)->String {
        self.borrow().type_name()
    }

        
    fn as_any(&self) -> &dyn Any{
        self
    }    
        
}


fn print_type_of<T>(_: &T) {
    println!("{}", std::any::type_name::<T>())
}

fn main() {
    
    // Example with 'raw' variables
    let mut values : Vec<&dyn ValueTrait> = Vec::new();
    let v = Function::Script(ScriptFn(2.0));
    values.push(&v);
    let function = values.pop().unwrap();
    
    match function.as_any().downcast_ref::<Function>() {
        Some(_) => {
            println!("Downcasting Value to Function worked!")
        },
        None => {
            panic!("Could not downcast Value to Function")
        }
    };
    
    
    // Example with Mutability Pattern
    let mut values : Vec<Rc<RefCell<dyn ValueTrait>>> = Vec::new();
    let v = Rc::new(RefCell::new(Function::Script(ScriptFn(2.0))));
    values.push(v);
    let function = values.pop().unwrap();
    
    let tmp = function.borrow();
    let f = match tmp.as_any().downcast_ref::<Function>() {
        Some(f) => {
            println!("Downcasting Rc<RefCell<Value>> to Function worked!");
            f
        },
        None => {
            panic!("Could not downcast Rc<RefCell<Value>> to Function");
        }
    };
    
    print_type_of(f);
    
}

Yup, dynamic dispatch has a significant performance impact because it interferes with function inlining: the gateway to all other optimizations.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.