Reputation: 22403
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
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