0

I'm working on a pre-existing project and need to add a field to structure Message which is a Box <dyn Any>.

Turning Box<dyn Any> into an enum is not possible for this project.

pub struct Message<PB: ProtocolBehavior> {
    /* */
    pub info: Box<dyn Any>,
}

Unfortunately, Message needs to be (de)serializable, and implement Hash, Clone, Debug, Display (the list goes on) ; therefore, so does the field info.

What I came up with was to create a new trait AnySerializable. I made it implement all traits necessary. (Thanks to dtolnay's typetag crate, I was able to make this new trait Serializable)

#[typetag::serde(tag = "type")]
pub trait AnySerializable: Any + core::fmt::Debug + std::fmt::Display + DynClone + DynHash {}

Then I modified the field info of Message.

pub struct Message<PB: ProtocolBehavior> {
    /* */
    pub info: Box<dyn AnySerializable>,
}

However, I need to be able to turn info into a Box<dyn Any>.

The following code gives me an error as the return types are mismatched.

impl Message {
    pub fn get_info(&self) -> Box<dyn Any> {
        self.info
    } 
}

So then I tried adding a method to my trait AnySerializable

#[typetag::serde(tag = "type")]
pub trait AnySerializable: Any + core::fmt::Debug + std::fmt::Display + DynClone + DynHash {
    fn as_any(&self) -> dyn Any;
}

along with this method for Message.

impl Message {
    pub fn get_info(&self) -> Box<dyn Any> {
        Box::new(self.info.clone().as_ref().as_any())
    } 
}

However I can't box dyn Any as its size is not known at compilation time.

But I would like to do something like this. Is there a way to do this, possibly with unsafe Rust ?

I tried replacing any occurrence of Any by AnySerializable in the project, but I get additional errors that seem very complicated. I will look into them if I can't elegantly get a Box<dyn Any>.

nimics
  • 31
  • 3
  • 1
    You're trying to return an owned value taken from `&self`, which is not going to work. You either need to clone it, take `&mut self` and remove it from self, take `self` and consume self, or return a reference like `&dyn Any`. – drewtato Jul 08 '23 at 21:23
  • Good point. I used clone to deal with this. Corrected it in post. However, the compiler still doesn't know the size of my `dyn Any` in advance, so I still can't box it. – nimics Jul 08 '23 at 21:33
  • It needs to be `fn as_any(&self) -> &dyn Any`. – Chayim Friedman Jul 09 '23 at 09:28

2 Answers2

0

You cannot currently upcast in stable rust, not even using unsafe [0][1]. note: upcasting is casting a child trait object (AnySerializable) into its supertrait object (Any).

I'm afraid the best path forward for you is to change many occurances of Any to AnySerializable. There is a feature in nightly rust that would allow this to work with minimal code changes, but based on its progress, I don't expect it to be stabalized in the next few releases.

#![feature(trait_upcasting)]

use std::any::Any;

trait AnySerializable: Any {
    fn serialize(&self) -> String;
}

impl<T:Any> AnySerializable for T {
    fn serialize(&self) -> String {"".to_string()}
}

fn main() {
    let serializable: Box<dyn AnySerializable> = Box::new(());
    let any: Box<dyn Any> = serializable as Box<dyn Any>;
}

[0] Why doesn't Rust support trait object upcasting?

[1] Is it possible to cast a trait object to another trait object?

asky
  • 1,520
  • 12
  • 20
  • Thanks a lot ! I have another issue with your answer however. [There's currently no way to implements (de)serialize for traits with generic impls](https://github.com/dtolnay/typetag/issues/1). I'm assuming the solution to this is to actually go ahead and replace all occurrences of Any with AnySerializable. I'll leave my question up a bit longer to see if anyone has anything else to add ! – nimics Jul 08 '23 at 22:07
  • I have not used the typetag crate before, I don't really know how to use it and I don't see any obvious way around the issue you linked. My only suggestion is if all you do with this info field is serialize and deserialize it and it can't be an enum, maybe a String is a better fit. – asky Jul 10 '23 at 10:13
0

I found this crate that does exactly what I want to do !

It allows downcast to Box<dyn Any>.

nimics
  • 31
  • 3
  • Can you provide code demonstrating how to use this crate? I'm skeptical because the documentation appears to only support downcasting (which would be `Any` to `AnySerialize`) and only to concrete types, but I'm happy to be proven wrong. – kmdreko Jul 08 '23 at 23:58
  • I ended up finding a much better strategy for what I wanted to do for my own code, so I ended up not using it. But I at least checked if I could cargo build with it, so it seems to work. I added `Downcast` to the traits implemented by `AnySerializable`. And I just needed to call `impl_downcast!` on a `Box` to get a `Box`. However I haven't tested it ! – nimics Jul 11 '23 at 12:08