Reputation: 174
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
Upvotes: 2
Views: 418
Reputation: 602285
A RefCell
is a wrapper struct facilitating interior mutability, while an Rc
is a reference-counted smart pointer. They are quite different and serve orthogonal purposes. If you want to use a trait object with interior mutability, you can combine Rc
and RefCell
to Rc<RefCell<dyn Test>>
.
A RefCell
provides a safe mechanism to borrow the wrapped value at runtime. Rust's requirements for references – mutable borrows are exclusive – are checked when you use the borrow()
or borrow_mut()
methods based on a flag stored in the RefCell
alongside the wrapped value.
An Rc
is a reference-counted smart pointer. It allows multiple owners for a heap-allocated value. Once all references are dropped, the pointee will be deallocated. As you can see, this functionality is unrelated to checking borrows at runtime.
If you wrap a dynamically sized type like a trait object inside a RefCell
, you end up with a dynamically sized RefCell
. In Rust, dynamically sized types cannot be stored on the stack, so you have to put it behind some kind of reference. In this case Rc
seems most appropriate, since a RefCell
is most useful when combined with shared ownership.
Related question:
This section in the book provides further explanations:
Upvotes: 10