Mark
Mark

Reputation: 722

Cloning an Rc pointer over a trait object in Rust?

I am learning Rust and don't understand why the following doesnt work. I gather we are unable to clone an Rc pointer over a trait object? How am I to pass such a reference to an function defined only by a trait, as attempted in some_function?

use std::rc::Rc;

trait SomeTrait {}
struct SomeThing {}
impl SomeTrait for SomeThing {}

fn some_function(s: Rc<dyn SomeTrait>) {}
fn another_function(s: &Rc<dyn SomeTrait>) {}

fn main() {
    let s = Rc::new(SomeThing{});

    // This doesnt work
    some_function(Rc::clone(&s));

    // I could do this
    some_function(s);

    // But then I could not do this
    some_function(s);
    
    // For that matter, neither can I do this
    another_function(&s);
}

Upvotes: 5

Views: 684

Answers (1)

Masklinn
Masklinn

Reputation: 42272

If you look at the error messages of the compiler you will see this:

error[E0308]: mismatched types
   |
14 |     some_function(Rc::clone(&s));
   |                             ^^ expected trait object `dyn SomeTrait`, found struct `SomeThing`
   |
   = note: expected reference `&Rc<dyn SomeTrait>`
              found reference `&Rc<SomeThing>`

That means the compiler mis-infers the type of s to Rc<SomeThing> instead of the Rc<dyn SomeTrait> you were looking for, which can be confirmed by the usual trick of providing a blatantly incorrect type to let s:

error[E0308]: mismatched types
  --> src/main.rs:11:17
   |
11 |     let s: () = Rc::new(SomeThing{});
   |            --   ^^^^^^^^^^^^^^^^^^^^ expected `()`, found struct `Rc`
   |            |
   |            expected due to this
   |
   = note: expected unit type `()`
                 found struct `Rc<SomeThing>`

Since Rust wrappers are invariant, an Rc<SomeThing> and an Rc<dyn SomeTrait> are completely incompatible values, there is no way to just use one for the other.

The solution is to simply explicitely type s correctly:

use std::rc::Rc;

trait SomeTrait {}
struct SomeThing {}
impl SomeTrait for SomeThing {}

fn some_function(s: Rc<dyn SomeTrait>) {}
fn another_function(s: &Rc<dyn SomeTrait>) {}

fn main() {
    let s: Rc<dyn SomeTrait> = Rc::new(SomeThing{});

    // This doesnt work
    some_function(Rc::clone(&s));
    
    // For that matter, neither can I do this
    another_function(&s);
}

Obviously the two calls in the middle can't work, as the first one will move the local Rc, which the second one (and the final call) still want.

Upvotes: 6

Related Questions