Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 261
Ian Henry Avatar asked Mar 02 '23 12:03

Ian Henry


1 Answers

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.

like image 136
orlp Avatar answered Mar 08 '23 16:03

orlp