Can't downcast Box<Any>


#1

Hi,

I’m trying to write a small framework and need a Box I can downcast for a cache. I ran into this problem and frewsxcv helped me with a small piece of code we both expected to work but it doesn’t:

use std::any::Any;
use std::fmt::Display;

fn main() {
    log(Box::new("hi"))
}

fn log<T: Any + 'static>(value: Box<T>) {
    let x = value.downcast();
}

[rust playground](https://play.rust-lang.org/?code=use%20std%3A%3Aany%3A%3AAny%3B use%20std%3A%3Afmt%3A%3ADisplay%3B fn%20main()%20{ %20%20%20%20log(Box%3A%3Anew("hi"))%0A%7D%0A%0Afn%20log%3CT%3A%20Any%20%2B%20%27static%3E(value%3A%20Box%3CT%3E)%20%7B%0A%20%20%20%20let%20x%20%3D%20value.downcast()%3B%0A%7D&version=nightly&backtrace=0)

It gives the error:

<anon>:9:19: 9:27 error: no method named `downcast` found for type `Box<T>` in the current scope
<anon>:9     let x = value.downcast();

Shouldn’t this work? If not is the documentation outdated? It says there should be a .downcast method for Box types.

Any feedback much appreciated!

Cheers,
Tinco


#2

That’s because downcast is implemented on Box<Any + 'static>, not Box<T> where T: Any + 'static. The first is a boxed trait object (a concrete type), the second is actually a class of types (all Box<T> where T has a 'static lifetime and implements Any). At compile time, log will be monomorphized and the type of value will be Box<&str>.

You can fix this by casting the Box<&str> to a Box<Any> (unsizing &str to Any):

use std::any::Any;
use std::fmt::Display;

fn main() {
    log(Box::new("hi"))
}

fn log<T: Any + 'static>(value: Box<T>) {
    let x: Result<Box<&str>, _> = (value as Box<Any + 'static>).downcast();
}

However, I doubt this is what you want to do. There’s no reason to downcast in this case because you already know the type (it’s T).

You probably intended to write:

use std::any::Any;
use std::fmt::Display;

fn main() {
    log(Box::new("hi"))
}

fn log(value: Box<Any + 'static>) {
    let x: Result<Box<&str>, _> = value.downcast();
}

If you find my explanation a bit dense/opaque, please say so. There’s a lot of jargon here.


#3

Ah thanks a lot! That makes total sense. Please allow me a follow up question, which is closer to my actual use case which also raises the no method downcast error:

use std::any::Any;
use std::fmt::Display;

fn main() {
    log(Box::new("hi"))
}

fn log(value: Box<Any + Sync + 'static>) {
    let x: Result<Box<&str>, _> = value.downcast();
    println!{"{}", *x.unwrap()};
}

When I add the Sync trait it doesn’t work anymore. I’m a bit confused by that, the adding of traits shouldn’t matter for this case should it?


#4

Same error as before, downcast is only implemented for particular variants of Box<Any>: https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.downcast

It works if you then manually cast to the appropriate type:

let x: Result<Box<&str>, _> = (value as Box<Any+'static>).downcast();

You can also write it with UFCS:

let x: Result<Box<&str>, _> = <Box<Any+'static>>::downcast(value);

I’m not sure why this version isn’t automatic:

let x: Option<&&str> = (&*value).downcast_ref();

#5

Alright, I guess that makes sense. I get a new exception now but I’ll make a new topic for it as it seems to be unrelated. Thanks!