How to do a std::any::Any::downcast(Box<dyn MyTrait>) or return Box<dyn MyTrait>?

How to handle downcasting such that in case of failure i can return argument that was passed in function call?

Code available in playground.

struct Obj;

fn cast_or_return(initial_obj: Box<dyn MyTrait>) -> Result<Box<Obj>, Box<dyn MyTrait>> {
    let any_obj: Box<dyn Any> = initial_obj;

    match any_obj.downcast::<Obj>() {
        | Ok(done) => {
            Ok(done)
        }

        | Err(another_obj) => {
            // How to upcast back to initial_object?
            let cast_back: Box<dyn MyTrait> = another_obj;
            Err(cast_back)

            //Ok(Box::new(Obj))
        }
    }

}

fn main() {
    let o: Box<dyn MyTrait> = Box::new(Obj);
    cast_or_return(o).ok();
}


use std::any::Any;
trait MyTrait: Any {}
impl MyTrait for Obj {}

I just can't come up with any sane way to solve this. Any ideas would be appreciated.

fn cast_or_return(initial_obj: Box<dyn MyTrait>) -> Result<Box<Obj>, Box<dyn MyTrait>> {
    if (&*initial_obj as &dyn Any).downcast_ref::<Obj>().is_some() {
        (initial_obj as Box<dyn Any>)
            .downcast::<Obj>()
            .map_err(|_| unreachable!())
    } else {
        Err(initial_obj)
    }
}

it's ugly, it does the chack twice (that will be optimized away in release mode), it has a very sus unreachable!, but it works and i don't think there is anything better

actually it seems it can't because of vtable sheninigans

Be it as it may, but for now this is the only solution that is not unsafe and works.

The unsafe version would be

    if (&*initial_obj as &dyn Any).type_id() == TypeId::of::<Obj>() {
        let ptr = Box::into_raw(initial_obj) as *mut Obj;
        // SAFETY: We just confirmed the `TypeId`
        unsafe {
            Ok(Box::from_raw(ptr))
        }
    } else {
        Err(initial_obj)
    }

This looks neat. How does Box::into_raw deal with fat pointer? Just forgets trait info and keeps pointer to struct?

dyn raw pointers are wide (include the vtable pointer still). That's what into_raw returns. The cast to *mut Obj is what discards the vtable pointer.

because you need a Result

oh yeah my bad