Ian Henry
Ian Henry

Reputation: 22403

How can I use Rc::clone while casting to a trait object?

The Rust book says that it's idiomatic to use Rc::clone(&x) rather than x.clone() with Rc values, so that it's obvious that this is not your typical clone. I'm all for this, but I'm having trouble applying the theory in practice.

I want to clone a reference-counted struct, but cast the clone to a trait object. I can do this using rc.clone(), but not by using Rc::clone(&rc). This is... strange to me.

struct ListView {}
trait View {}
impl View for ListView {}

fn very_contrived_example() {
    let list_view: Rc<ListView> = Rc::new(ListView {});
    let mut views: Vec<Rc<dyn View>> = Vec::new();

    // Using Rc::clone does not work:

    // error[E0308]: mismatched types
    //
    // views.push(Rc::clone(&list_view));
    //                      ^^^^^^^^^^ expected trait object `dyn View`, found struct `ListView`
    //
    // note: expected reference `&Rc<dyn View>`
    //          found reference `&Rc<ListView>`

    // Using a cast works in this very contrived example, but has the
    // disadvantage of moving `list_view`, for some reason, which is not
    // acceptable in general:
    // views.push(Rc::clone(&(list_view as Rc<dyn View>)));

    // But invoking it using method syntax works fine, without a move:
    views.push(list_view.clone());
}

What is the difference between Rc::clone(&x) and x.clone()? What function is x.clone() actually invoking? What is the type of self? Can I invoke it directly?

What is the idiomatic way to write this?

Upvotes: 4

Views: 210

Answers (1)

orlp
orlp

Reputation: 117681

This is a rare failure of type inference. Explicitly passing the correct explicit type works:

views.push(Rc::<ListView>::clone(&list_view))

The problem is that Rc::clone(&list_view) infers the T in Rc<T> based on the expected type (which is Rc<dyn View>), not on the argument type. On the other hand, when you call list_view.clone() it uses the implementation of Clone on the type of list_view and thus it resolves to Rc::<ListView>::clone.


If the above problem occurs very often in your code and you would like to keep a visual distinction between normal clones and refcounted shallow clones, you can write a little helper trait:

trait RcClone : Clone {
    fn rc_clone(&self) -> Self {
        self.clone()
    }
}

impl<T: ?Sized> RcClone for Rc<T> { }
impl<T: ?Sized> RcClone for Arc<T> { }

Then you can write list_view.rc_clone() in your code base which will only work for refcounted types. This still indicates the semantics different from the regular clone while not having the type inference issues.

Upvotes: 8

Related Questions