5

I have a Vec<Box<dyn Trait>> as input, and I want to store its elements in a Vec<Rc<RefCell<dyn Trait>>>. What is the best way to do it?

I tried with:

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

trait Trait {}

fn main() {
    let mut source: Vec<Box<dyn Trait>> = Vec::new();
    let mut dest: Vec<Rc<RefCell<dyn Trait>>> = Vec::new();

    for s in source {
        let d = Rc::new(RefCell::new(s.as_ref()));
        dest.push(d);
    }
}

But I got the error:

error[E0277]: the trait bound `&dyn Trait: Trait` is not satisfied
  --> src/main.rs:12:19
   |
12 |         dest.push(d);
   |                   ^ the trait `Trait` is not implemented for `&dyn Trait`
   |
   = note: required for the cast to the object type `dyn Trait`

Is it actually possible or am I required to change the input type?

Nick
  • 10,309
  • 21
  • 97
  • 201
  • 2
    The problem isn't the casting, it's that a `RefCell` needs to own its data, so you can't construct a `RefCell` at all. You will need to change the type to something like `Vec>>>` – Peter Hall May 23 '20 at 18:07
  • 1
    Or even `Vec>>`, I saw there is a conversion `Rc::from(Box)`. – Nick May 23 '20 at 18:19

2 Answers2

1

Although RefCell<dyn Trait> is a valid type, since the declaration of RefCell<T> allows T: ?Sized, there does not currently appear to be a way to create one from outside the module, other than CoerceUnsized, which requires starting with a sized value.

However, you should be able to use unsafe code to convert to a Cell or UnsafeCell, since both have #[repr(transparent)].

Solomon Ucko
  • 5,724
  • 3
  • 24
  • 45
  • 1
    Well, you can create one with an unsized coercion, but that won't help the OP, as they start with an unsized value. – rodrigo May 23 '20 at 21:56
1

If you control Trait, one option is to simply implement it for Box<dyn Trait> by deferring to the inner implementation:

// We could implement Trait only for Box<dyn Trait>, but usually what you want
// is to implement it for all Boxes of things that are Trait instead
impl<T: ?Sized + Trait> Trait for Box<T> {}

fn pushes(dest: &mut Vec<Rc<RefCell<dyn Trait>>>, source: Vec<Box<dyn Trait>>) {
    for s in source {
        dest.push(Rc::new(RefCell::new(s)));
    }
}

Be aware this wraps the already-Boxed object behind a second pointer (Rc) so if you are using dest in a performance-sensitive algorithm it will have to dereference it twice rather than once. If you are able to restructure the code so you can accept Box<T: Trait>, you could eliminate the double indirection by moving the T out of the Box and into a RefCell.

Related questions

trent
  • 25,033
  • 7
  • 51
  • 90