Downcast Generic Struct instance with Trait Object

Given this:

    trait Trait { }
    struct A {
    }
    impl Trait for A {}
    struct B<T> {
        value: T
    } 

I understand it's possible to downcast pointer of dyn Trait to pointer of A, e.g. Box<dyn Trait> to Box<A> (link).
My question is: Is possible to downcast from B<dyn Trait> to B<A>?

Well, B isn’t a smart pointer type or anything, unlike Box. In your example code, it contains T directly, you won’t even be able to obtain a B<dyn Trait>. But perhaps you actually had a different, useful, type other than B in mind where the question makes sense. So please specify your use case more precisely.

Thanks for the reply @steffahn . In my real use case, B looks like:

struct Dependency<T> {
    innter: Arc<T>
}

This is obtainable by a few different ways, such as impl From<Box<dyn Trait>> for Dependency<T>.

I have a collection of Dependency<dyn Trait> and now I want to downcast them to concrete types. (I have access to all concrete types).
I am open to any means on this, including unsafe or other memory hack (I heard mem::transmute might help?).

Not sure if this is precise enough? Please let me know. Thanks.

No, no, no, try to avoid transmute at all costs. It is almost never what you need. For example in this case note that an Arc<dyn Trait> has a different memory layout than Arc<T> for a T: Trait (the pointers to unsized types are bigger).

As mentioned in a comment in the link you had in your original post, downcast-rs may be an option to use. It includes functionality to downcast an Arc. You can use that to add a method to Dependency yourself that offers downcasting capabilities.

No, no, no, try to avoid transmute at all costs

what about unsafe?

As mentioned in a comment in the link you had in your original post, downcast-rs may be an option to use. It includes functionality to downcast an Arc . You can use that to add a method to Dependency yourself that offers downcasting capabilities.

I read their source code and I doubt if it will work for my use case though but I'll keep trying.

Do you know the core mechanism that enables this use case?

Here’s a code example to demonstrate what I mean

use downcast_rs::{impl_downcast, DowncastSync};
use std::sync::Arc;
struct Dependency<T: ?Sized> {
    inner: Arc<T>,
}

impl<T: ?Sized> Dependency<T> {
    fn new(x: Arc<T>) -> Self {
        Dependency { inner: x }
    }
}
impl Dependency<dyn Trait> {
    fn downcast<T: Trait>(self) -> Result<Dependency<T>, Dependency<dyn Trait>> {
        match self.inner.downcast_arc() {
            Ok(result) => Ok(Dependency { inner: result }),
            Err(result) => Err(Dependency { inner: result }),
        }
    }
}

trait Trait: DowncastSync {}
impl_downcast!(sync Trait);

struct St;
impl Trait for St {}

fn main() {
    let x: Arc<dyn Trait> = Arc::new(St);
    let y = Dependency::new(x);
    assert!(matches!(y.downcast::<St>(), Ok(_)));
}

hmm.. I managed to get by entirely without any extra crates, which makes me question some design decisions of downcast-rs. But I’m also not really following its main use-case anways.

Here’s the code
use std::any::Any;
use std::sync::Arc;

struct Dependency<T: ?Sized> {
    inner: Arc<T>,
}

impl<T: ?Sized> Dependency<T> {
    fn new(x: Arc<T>) -> Self {
        Dependency { inner: x }
    }
}

impl<T: IntoAnyArc + 'static + ?Sized> Dependency<T> {
    fn downcast<S: Any + Send + Sync>(self) -> Result<Dependency<S>, Dependency<T>> {
        if self.inner.as_any().is::<S>() {
            Ok(Dependency {
                inner: self.inner.into_any_arc().downcast().unwrap(),
            })
        } else {
            Err(self)
        }
    }
}

trait IntoAnyArc {
    fn as_any(&self) -> &dyn Any;
    fn into_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
}
impl<T: Any + Send + Sync> IntoAnyArc for T {
    fn as_any(&self) -> &dyn Any {
        self
    }
    fn into_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
        self
    }
}

// IntoAnyArc is like Any + Send + Sync
// but it makes "dyn Trait" offer two more methods in the vtable
trait Trait: IntoAnyArc {}

struct St;
impl Trait for St {}

fn main() {
    let x: Arc<dyn Trait> = Arc::new(St);
    let y1 = Dependency::new(x.clone());
    let y2 = Dependency::new(x);
    assert!(matches!(y1.downcast::<St>(), Ok(_)));
    assert!(matches!(y2.downcast::<()>(), Err(_)));
}

(⟶ same thing in the playground)

I’m not 100% sure what you refer to by “core mechanisms” but perhaps the example above of how to do it without any extra crates might help you understand.

2 Likes

Yes, that's exactly what I need.

I didn't know we can do this:

fn into_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;

I had a hard time finding the right spell :sweat_smile:.

I really appreciated it mate!

Hi @steffahn, sorry for bothering you again, do you think if I can achieve the same thing with inner being inner: Arc<RwLock<T>>?

I tried changing

impl<T: Any + Send + Sync> IntoAnyArc for T 

to

impl<T: Any + Send + Sync + ?Sized> IntoAnyArc for T 

but it would cause compiler to complaint size of values of type T cannot be known at compilation time.

I also tried

impl<T: Any + Send + Sync + ?Sized> IntoAnyArc for RwLock<T> 

It would make me encountering the size issue in some other part of the system.

Just want to pick your brain to see a big picture about it :pray::pray:. Maybe I am on the wrong path entirely? Maybe I could create a custom smart pointer type that combines both the behavior of Arc and RwLock?

I’m having trouble finding a solution that does not need locking. Technically it “should” be possibly. A type &RwLock<dyn Trait> should (as far as I know) actually contain the vtable for Trait in the reference to the RwLock. However there seems to be no way to access it.

I did some googling and found the #![feature(arbitrary_self_types)] discussion that (as far as I can tell) includes a similar argument about types like RefCell in the end.

If you are interested in a solution that does need to lock the RwLock, I could try.

Aside

The purpose of downcast-rs is to precisely write for you the IntoAnyArc and friends. Granted, it is quite simple to write, but knowing how the helper trait should be written is nevertheless not obvious for everybody. I consider that to be a convenience crate much like the "read a single line of already parsed input" crates out there :wink:

Trying to avoid locking by all means possible (without waiting for these features to arrive), I went ahead and wrote what is probably the most unsafe piece of Rust code I’ve ever produced. (Coming to a point where even transmute was unhappy without some trickery).

Thanks steffahn for making this effort. What you wrote definitely went way beyond my limited knowledge of Rust :sweat_smile:.

I guess now it's safe to say I shouldn't try this path. I shouldn't encourage a so called dependency to be mutable anyway. Originally, the only reason to make it mutable was because I need to start/stop dependency at the start/end of program. I guess I can just abuse Arc's get_mut without locking if I can get topology right.

Thanks again!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.