Указатель клона и Cast Rc - PullRequest
       313

Указатель клона и Cast Rc

0 голосов
/ 30 апреля 2019

это своего рода дополнительный вопрос из этого: Ржавый динамический объект типа черты между разными тайтами

Решение, предоставленное там, работает очень хорошо, когда мы используем ссылки для объектов признаков.

На этот раз я пытался сделать то же самое с указателями Rc. Например

  • У меня есть супер-черта с именем TraitAB и 2 черты с именами TraitA и TraitB
  • Поэтому, когда я впервые создаю объект черты типа TraitAB вместо использования Box, теперь я использую указатель Rc.
  • Мне нужна переменная типа TraitA, которая будет ссылаться на ab

Здесь я сделал очень минимальный пример:

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();
    }
}

Это не работает, хотя. По сообщениям об ошибках:

xx |     fn as_a(&self) -> Rc<dyn TraitA> {Rc::clone(self)}
   |                                                 ^^^^ expected struct `std::rc::Rc`, found struct `MyType`
   |
   = note: expected type `&std::rc::Rc<dyn TraitA>`
              found type `&MyType`

методы as_a и as_b не могут знать, что self на самом деле является указателем Rc. Есть ли способ выполнить приведение клонированного общего указателя?

Спасибо

Ответы [ 2 ]

2 голосов
/ 30 апреля 2019

методы as_a и as_b не могут знать, что self на самом деле является указателем Rc.

На самом деле это не так! Существует редко используемая функция , которая позволяет использовать self в качестве различных стандартных типов ссылок (Rc<Self>, Box<Self> и т. Д.).

Это означает, что вы можете переписать свой TraitAB как

trait TraitAB : TraitA + TraitB {
    fn as_a(self: Rc<Self>) -> Rc<dyn TraitA>;
    fn as_b(self: Rc<Self>) -> Rc<dyn TraitB>;
}

К сожалению, как написано, as_a и as_b перемещаются self: Rc<Self>, поскольку Rc<T> не реализует Copy (только Clone). Один из способов исправить это - просто клонировать ab перед передачей в эти методы. Это также означает, что вам не нужно клонировать self внутри метода. (ссылка на игровую площадку)

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();

Используя ночную функцию arbitrary_self_types, можно заставить as_a и as_b принимать себя как &Rc<Self> (что странно для меня, так как это ссылка на ссылку). Это позволяет вызывать ab.as_a() без перемещения ab. Единственная проблема с этим подходом состоит в том, что TraitAB больше не является объектобезопасным 1 , поэтому Rc<dyn TraitAB> больше не работает. (ссылка на игровую площадку) .


  1. Согласно проблема отслеживания для произвольных типов , вопрос безопасности объекта все еще открыт. Я не совсем уверен, какие правила сейчас.
1 голос
/ 30 апреля 2019

Вам нужно реализовать TraitAB на 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();
    }
}

Кстати, я не вижу причин для TraitAB продления TraitA + TraitB но вы также можете расширить и реализовать TraitA и TraitB для Rc<MyType>.

Рабочий пример с реализованными функциями для TraitA и TraitB можно найти здесь: Детская площадка

...