Why I can use dynamic dispatch (dyn MyTrait) with Rc but not RefCell?

I was using Rc for dynamic dispatch on an object that implements a trait. Then I needed interior mutability so I changed to RefCell. I thought that RefCell was simply an Rc for interior mutability, but it won't accept my trait object.

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

trait Test {
    
}

struct A {
    
}

impl Test for A {
    
}

fn main(){
    //This works:
    let x: Rc<dyn Test> = Rc::new(A{});
    
    //But this not:
    //let x: RefCell<dyn Test> = RefCell::new(A{});
}

Error:

error[E0308]: mismatched types
  --> src/main.rs:18:32
   |
18 |     let x: RefCell<dyn Test> = RefCell::new(A{});
   |            -----------------   ^^^^^^^^^^^^^^^^^ expected trait object `dyn Test`, found struct `A`
   |            |
   |            expected due to this
   |
   = note: expected struct `std::cell::RefCell<dyn Test>`
              found struct `std::cell::RefCell<A>`

error[E0277]: the size for values of type `dyn Test` cannot be known at compilation time
  --> src/main.rs:18:9
   |
18 |     let x: RefCell<dyn Test> = RefCell::new(A{});
   |         ^ doesn't have a size known at compile-time
   |
   = help: within `std::cell::RefCell<dyn Test>`, the trait `std::marker::Sized` is not implemented for `dyn Test`
   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
   = note: required because it appears within the type `std::cell::RefCell<dyn Test>`
   = note: all local variables must have a statically known size
   = help: unsized locals are gated as an unstable feature

error: aborting due to 2 previous errors; 1 warning emitted

RefCell stores its contents inline, without any pointer indirection. So RefCell<dyn Trait> is a dynamically-sized type, just like dyn Trait itself. This means you can't store it directly in a variable, since variables in Rust must have a size known at compile time.

Instead, it has to be behind some sort of pointer type, such as a reference (&RefCell<T>), raw pointer (*const RefCell<T>), or smart pointer (Box<RefCell<T>> or Rc<RefCell<T>>). This works, for example:

let cell = RefCell::new(A {});
let x: &RefCell<dyn Test> = &cell;
2 Likes