Handle downcast error when downcasting Ref<Box<dyn Any>> into Ref<Box<T>>

1.3k views Asked by At

I need to write a function foo that takes a &RefCell<Box<dyn Any>>, borrows from the RefCell and returns a downcasted object. The downcasted type is chosen at runtime, but for this example let's assume it's usize.

use core::any::Any;
use std::cell::{RefCell, Ref};

pub fn foo<T: 'static>(cell: &RefCell<Box<dyn Any>>) -> Option<Ref<Box<T>>> {
    ???
}

pub fn main() {

    let boxed: Box<dyn Any> = Box::new(1 as usize);
    let cell = RefCell::new(boxed);

    let num = foo(&cell);
    println!("x: {}", num.unwrap());
}

I tried implementing foo like this:

// 1:
pub fn foo<T: 'static>(cell: &RefCell<Box<dyn Any>>) -> Option<Ref<Box<T>>> {
    let borrowed_cell = Ref::map(cell.borrow(), |borrow| borrow.downcast_ref::<T>().unwrap());
    Some(borrowed_cell)
}

The problem with this version is that it assumes that downcast_ref will always work, but I would like to catch a downcast_ref error. Below I try to implement foo in a way that I can catch the error:

// 2:
pub fn foo<T: 'static>(cell: &RefCell<Box<dyn Any>>) -> Option<Ref<T>> {
    {
        cell.borrow().downcast_ref::<T>()?;
    }

    let borrowed_cell = Ref::map(cell.borrow(), |borrow| borrow.downcast_ref::<T>().unwrap());
    Some(borrowed_cell)
}

This version can catch the downcast error, but it has to call downcast_ref twice (which could be acceptable, but I am wondering if there is a better way). When trying to use downcast_ref only once, I got stuck with lifetime errors.

1

There are 1 answers

2
Locke On BEST ANSWER

After a bit of tinkering with it, I came up with this solution. You can make use of Any::is<T>() to check the borrow before mapping it.

pub fn foo<T: 'static>(cell: &RefCell<Box<dyn Any>>) -> Option<Ref<T>> {
    let borrowed = cell.borrow();
    
    if borrowed.is::<T>() {
        Some(Ref::map(borrowed, |x| x.downcast_ref::<T>().unwrap()))
    } else {
        None
    }
}

Rust Playground link