Alternatives to `dyn Any` `downcast` for types with non-static lifetimes that reference another type?

The definition of A uses A(&'static str) instead of A<'a>(&'a str) in order to get compatible with Any. Even though, the way A and B operate makes it impossible (I believe) for B to be 'static.

use downcast_rs::{Downcast, impl_downcast};

trait Trait: Downcast {
    fn do_it(&self) -> Box<dyn Trait>;
}
impl_downcast!(Trait);

#[derive(Clone, Debug)]
struct A(&'static str);

impl Trait for A {
    fn do_it(&self) -> Box<dyn Trait> {
        println!("A: {}", self.0);
        Box::new(B(self))
    }
}

struct B<'a>(&'a A);

impl<'a> Trait for B<'a> {
    fn do_it(&self) -> Box<dyn Trait> {
        println!("B: {:?}", self.0);
        Box::new(self.0.clone())
    }
}

fn main() {
    let s = "aaaaa";
    let a: Box<dyn Trait> = Box::new(A(s));
    let b: Box<dyn Trait> = a.do_it();
    let a2: Box<dyn Trait> = b.do_it();
    let b2: Box<dyn Trait> = a2.do_it();
    b2.do_it();
}


/*
Expected output:
A: aaaaa
B: A("aaaaa")
A: aaaaa
B: A("aaaaa")
*/

Is there an alternative or workaround for this complex usecase? Like imagine A randomly returns B(&self) or C(&self), and C(a).do_it() randomly returns B(a) or C(a), etc.

Also please feel free to point out what else is wrong with the code.

1 Like

the best way to do this sort of thing would be an enum.

if you really cannot do an enum, then you should consider switching from normal reference to reference counted ones.

if you really can't do that, you can look at better_any - Rust that supports trait objects with 1 lifetime

2 Likes

Very intuitive too, but may not be what you would want if you want the pattern to be extensible.

#[derive(Clone, Debug)]
struct A<'a>(&'a str);

#[derive(Debug)]
struct B<'a>(&'a A<'a>);

enum Value<'a> {
    A(A<'a>),

    B(B<'a>),
}

impl<'a> Value<'a> {
    fn do_it(&'a self) -> Value<'a> {
        match self {
            Value::A(a) => {
                println!("A: {}", a.0);

                Value::B(B(a))
            }

            Value::B(b) => {
                println!("B: {:?}", b.0);

                Value::A(b.0.clone())
            }
        }
    }
}

fn main() {
    let s = "aaaaa";
    let a = Value::A(A(s));
    let b = a.do_it();
    let a2 = b.do_it();
    let b2 = a2.do_it();
    b2.do_it();
}
1 Like

Follow-up post.

Second follow-up: Made it without dyn Any. Thanks for the advice!

(The code needs to be run locally because the playground doesn't have downcast_rs.)