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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With