A little problem with &dyn std::any::Any

In the following code, I don't understand the difference between p_a_any1 (accepted) and p_a_any2 (refused by compiler).
What is the mentioned borrowing problem ?

trait A : std::any::Any {
  fn f(&self) -> ();
  fn as_any(&self) -> &dyn std::any::Any;
}

struct S {
}

impl A for S {
  fn f(&self) {println!("I'm an A");}
  fn as_any(&self) -> &dyn std::any::Any {self}
}

fn main() {
  let s = S {};

  let p_any:&dyn std::any::Any = &s;
  
  if p_any.is::<S>() {
    println!("I'm an S")
  }
  
  let p_a:&dyn A = &s;
  let p_a_any1 = p_a.as_any();//OK
  if p_a_any1.is::<S>() {
    println!("I'm an S")
  }

  let p_a:&dyn A = &s;
  let p_a_any2:&dyn std::any::Any = &p_a;//refused ???
  if p_a_any2.is::<S>() {
    println!("I'm an S")
  }
}

In this line, you've taken a reference to p_a, which is already a reference, i.e. you have created a value of type &'_ &'_ S. Then you attempt to coerce it to &'_ dyn Any, but only 'static types implement Any, so it assumes that the inner reference must be 'static, i.e. p_a_any2 is a coerced &'_ &'static S. Then you get an error because the S referred to is a local variable, which doesn't live for 'static.

If there wasn't a lifetime error (e.g. by declaring a static instance of the S type), then this code still wouldn't do what you want, because p_a_any2.is::<S>() would be false and p_a_any2.is::<&'static S>() would be true.

Your working line let p_a_any1 = p_a.as_any() is not creating any nested references; it's coercing &S to &dyn Any inside of the as_any() method, so there's always only one level of reference.


The line of code that does not use as_any() and does not produce any double-references would be:

let p_a_any2: &dyn std::any::Any = p_a;

But this does not succeed because Rust does not currently support trait upcasting — coercion from one dyn type to another dyn type. The entire point of the pattern of creating an as_any() method is to work-around this problem by introducing a manual implementation of upcasting: putting a function in the dyn A vtable that can produce the necessary dyn Any vtable.

Hopefully trait upcasting will be finished and stabilized soon, and then we won't need any more as_any() methods. But for now, they're necessary to write this kind of code.

2 Likes

This is really the part I needed to have. Cheers !

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.