This is a follow up question from Rust dynamic cast trait object between different taits . The solution provided there works really well when we use references for trait objects, but this time I am trying to do the same with Rc
pointers. For example
TraitAB
and 2 traits named TraitA
and TraitB
TraitAB
instead of using a Box
, now I use an Rc
pointer.TraitA
to be a reference of ab
Here I made a very minimal example:
use std::rc::Rc;
trait TraitAB: TraitA + TraitB {
fn as_a(&self) -> Rc<dyn TraitA>;
fn as_b(&self) -> Rc<dyn TraitB>;
}
trait TraitA {}
trait TraitB {}
struct MyType {}
impl TraitAB for MyType {
fn as_a(&self) -> Rc<dyn TraitA> {
Rc::clone(self)
}
fn as_b(&self) -> Rc<dyn TraitB> {
Rc::clone(self)
}
}
impl TraitA for MyType {}
impl TraitB for MyType {}
fn main() {
let a: Rc<dyn TraitA>;
let b: Rc<dyn TraitB>;
{
let mut ab: Rc<dyn TraitAB> = Rc::new(MyType {});
a = ab.as_a();
b = ab.as_b();
}
}
This doesn't work though. According to the error messages:
error[E0308]: mismatched types
--> src/main.rs:15:19
|
15 | Rc::clone(self)
| ^^^^ expected struct `std::rc::Rc`, found struct `MyType`
|
= note: expected reference `&std::rc::Rc<dyn TraitA>`
found reference `&MyType`
error[E0308]: mismatched types
--> src/main.rs:18:19
|
18 | Rc::clone(self)
| ^^^^ expected struct `std::rc::Rc`, found struct `MyType`
|
= note: expected reference `&std::rc::Rc<dyn TraitB>`
found reference `&MyType`
as_a
and as_b
can't know self is actually an Rc
pointer. Is there a way to do the cast of a cloned shared pointer?
methods
as_a
andas_b
can't know self is actually anRc
pointer.
Actually, that's not true! There's a rarely used feature that allows self
to be taken as various standard kinds of references ( Rc<Self>
, Box<Self>
, etc.).
That means that you can rewrite your TraitAB
as
trait TraitAB : TraitA + TraitB {
fn as_a(self: Rc<Self>) -> Rc<dyn TraitA>;
fn as_b(self: Rc<Self>) -> Rc<dyn TraitB>;
}
Unfortunately, as written, as_a
and as_b
move self: Rc<Self>
, since Rc<T>
doesn't implement Copy
(only Clone
). One way to fix this is to simply clone ab
before passing it into these methods. This also means that you don't need to clone the self
inside the method. (playground link)
let ab: Rc<dyn TraitAB> = Rc::new(MyType{});
let _a: Rc<dyn TraitA> = ab.clone().as_a();
let _b: Rc<dyn TraitB> = ab.clone().as_b();
Using the nightly-only feature arbitrary_self_types
, it's possible to make as_a
and as_b
take self as &Rc<Self>
(which looks weird to me since it's a reference to a reference). This allows ab.as_a()
to be called without moving ab
. The only problem with this approach is that TraitAB
is no longer object-safe 1 , so Rc<dyn TraitAB>
no longer works. (playground link) .
You need to implement TraitAB
on RC<MyType>
:
use std::rc::Rc;
trait TraitAB {
fn as_a(&self) -> Rc<dyn TraitA>;
fn as_b(&self) -> Rc<dyn TraitB>;
}
trait TraitA {}
trait TraitB {}
struct MyType {}
impl TraitAB for Rc<MyType> {
fn as_a(&self) -> Rc<dyn TraitA> {
Rc::clone(self) as Rc<dyn TraitA>
}
fn as_b(&self) -> Rc<dyn TraitB> {
Rc::clone(self) as Rc<dyn TraitB>
}
}
impl TraitA for MyType {}
impl TraitB for MyType {}
fn main() {
let a: Rc<dyn TraitA>;
let b: Rc<dyn TraitB>;
{
let mut ab: &TraitAB = &Rc::new(MyType {});
a = ab.as_a();
b = ab.as_b();
}
}
By the way, I don't see any reason for TraitAB
to extend TraitA + TraitB
, but you can extend and implement TraitA
and TraitB
for Rc<MyType>
as well.
This is a working example with the implemented functions for TraitA
and TraitB
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.