Rc<RefCell<Box< STRUCT >>> as Rc<RefCell<Box< TRAIT >>>


#1

I would like to represent a tree of signal filter algorithms as a tree of trait objects which can be rearranged at runtime. Most likely, my approach is too much influenced by c++ habits, anyway I stumbled over the question how one can create
Rc<RefCell<Box< STRUCT which implements TRAIT >>>
and be able to hand out
Rc<RefCell<Box<TRAIT>>>
and also
&Rc<RefCell<Box<TRAIT>>>
to others.

#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(non_snake_case)]

use std::rc::Rc;
use std::cell::RefCell;

trait Trait {
}

struct Struct {
  payload: f32,
}

impl Trait for Struct {
}


fn not_working() {
  let r1 = Rc::new(RefCell::new(Box::new(Struct { payload: 0.55 })));

  // I try this, but it errors:
  //let r2 = &r1 as &Rc<RefCell<Box<Trait>>>;

  // Another try, but errors:
  //let r2: &Rc<RefCell<Box<Trait>>> = &r1;

  // Errors as well:
  //let r2 = r1.clone() as Rc<RefCell<Box<Trait>>>;
}

I’d be happy to learn how one can make this work.


#2

From the book:

A trait object can be obtained from a pointer to a concrete type that implements the trait by casting it (e.g. &x as &Foo) or coercing it (e.g. using &x as an argument to a function that takes &Foo).
These trait object coercions and casts also work for pointers like &mut T to &mut Foo and Box to Box, but that’s all at the moment.

You can use static dispatch to do something like that:

fn main() {
    let r1 = Rc::new(RefCell::new(Box::new(Struct { payload: 0.55 })));
    foo(&r1);
}

fn foo<T: Trait>(r: &Rc<RefCell<Box<T>>>) {}

But what you have written, using &Trait or Box<Trait>, requires dynamic dispatch. The Struct object will be hidden behind a TraitObject. So to do what you’re trying to do, Rust would have to mutate r1, moving out Struct and replacing it with the TraitObject. It would make it impossible to make r1 immutable, and also change r1's type when taking a reference to it and coercing/casting it to contain a trait object.


#3

[quote=“Letheed/The Book, post:2, topic:7719”]
These trait object coercions and casts also work for pointers like &mut T to &mut Foo and Box to Box, but that’s all at the moment.
[/quote]That’s not quite true: While the CoerceUnsized trait is still unstable to implement, the stable compiler will happily use it for DST coercions.
edit: flagged in the book repo

@literda: Unfortunately, it turns out that while Rc and Box implement CoerceUnsized right now, the impl for RefCell hasn’t landed in stable yet. So there’s no stable way to do this.


#4

So if I get this right, the coercion is possible but only from an owned Box/Rc to another owned Box/Rc.