Search code examples
rusttraitsrefdowncastrefcell

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


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.


Solution

  • 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