Указание связанного типа в признаке, который наследуется от другого признака - PullRequest
0 голосов
/ 16 декабря 2018

Я начал работать над своим первым более амбициозным проектом Rust и боролся с чем-то, с чем я не сталкивался ни в одном из ресурсов и учебных пособий, которые использовал для изучения.Название вопроса отражает абстрактную проблему, но для примеров я буду использовать конкретные примеры, с которыми я борюсь.

Для моего проекта мне нужно взаимодействовать с различными сторонними сервисами, и я решилиспользовать фреймворк actix в качестве абстракции для разных участников моего домена.Каркас определяет черту Actor, которая должна быть реализована:

use actix::prelude::*;

struct MyActor {
    count: usize,
}

impl Actor for MyActor {
    type Context = Context<Self>;
}

У меня есть собственная черта, которая определяет интерфейс для сторонних интеграций.Давайте назовем это Client.Я хочу, чтобы каждый клиент вел себя как актер.

use actix::Actor;

pub trait Client: Actor {}

Где-то еще у меня есть вектор, в котором хранятся ссылки на все активные клиенты в системе.Когда я компилирую код, я получаю следующую ошибку:

error[E0191]: the value of the associated type `Context` (from the trait `actix::actor::Actor`) must be specified
  --> transponder/src/transponder.rs:15:26
   |
15 |     clients: Vec<Box<Client>>
   |                      ^^^^^^ missing associated type `Context` value

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

  • Я пыталсячтобы указать тип в признаке, но получил associated type defaults are unstable как ошибку.
  • Я попытался указать тип в реализации признака (impl Simulation), но получил associated types are not allowed in inherent impls как ошибку.
  • Я пробовал кое-что с impl <T: Actor> Simulation for T (например, как показано здесь ), но ничего не получалось.

Я предполагаю, что мне не хватает важной части знанийо чертах и ​​типах.Я был бы очень признателен, если бы кто-нибудь мог помочь мне решить мою проблему и указать мне в направлении недостающей части головоломки.Я чувствую, что здесь есть важный урок о Rust, который я действительно хочу выучить.

Ответы [ 2 ]

0 голосов
/ 16 декабря 2018

Насколько я понимаю из вашего кода, ваша главная цель - добавить всех клиентов / субъектов в коллекцию и вызывать обычное поведение, когда это необходимо. Но из-за безопасности объектов это невозможно, поэтому мы можем сделать это с небольшим взломом (создавая черту, которая имитирует Клиента, я назвал ее ClientProxy ).

У меня есть собственная черта, которая определяет интерфейс для сторонних интеграций.Давайте назовем это Клиентом.Я хочу, чтобы каждый клиент вел себя как актер.

pub trait Client: Actor {}

Да, это работает так, на самом деле это означает, что если какая-то структура имеет реализацию Client, она должна также иметь реализацию Actor.

Предположим, у нас есть два Context MyActor и OtherActor и их реализации Client / Actor.И у нас есть поведение в клиенте (поведение_like_client (& self)).

pub trait Client: Actor {
    fn behave_like_a_client(&self);
}

struct MyActor {
    count: usize,
} 

impl Actor for MyActor {
    type Context = Context<Self>;
}
impl Client for MyActor {
    fn behave_like_client(&self) {
        println!("I am MyActor as Client, and my count is {}", self.count);
    }
}

struct OtherActor {
    count: usize,
}

impl Actor for OtherActor {
    type Context = Context<Self>;
}
impl Client for OtherActor {
    fn behave_like_client(&self) {
        println!("I am OtherActor Client, and my count is {}", self.count);
    }
}

Теперь у нас есть наши актеры для тестирования, но давайте вернемся к нашей проблеме безопасности объектов, мы не можем собрать этих клиентов в одинколлекция.Вот почему я создал ClientProxy для имитации Client и хочу реализовать ClientProxy на Clients.Мы можем сделать это с помощью реализации ClientProxy в Generics, которая расширяет возможности клиентов:

//ClientProxy must have all behaviors in Client 
trait ClientProxy {
    fn behave_like_client(&self);
}

//This code implements ClientProxy to all Client like Objects
impl<T> ClientProxy for T
where
    T: Client,
{
    fn behave_like_client(&self) {
        self.behave_like_client();
    }
}

Теперь все готово, мы можем проверить нашу структуру:

struct Container {
    clients: Vec<Box<ClientProxy>>,
}

fn main() {
    let mut container = Container {
        clients: Vec::new(),
    };
    let a = Box::new(MyActor { count: 3 });
    let b = Box::new(OtherActor { count: 4 });

    container.clients.push(a);
    container.clients.push(b);

    container
        .clients
        .iter()
        .for_each(|a| a.behave_like_client());
    //output : 
    //I am MyActor as Client, and my count is 3
    //I am OtherActor Client, and my count is 4
}

Вы можете получить полный кодс детская площадка (не работает на детской площадке из-за отсутствия зависимости)

0 голосов
/ 16 декабря 2018

Подписи для всех методов всех элементов в коллекции должны быть идентичными, чтобы их можно было использовать взаимозаменяемо.Это означает, что связанные типы каждого элемента также должны быть одинаковыми.

Вы можете избавиться от этой ошибки, предоставив конкретный тип для Context связанного типа:

Vec<Box<dyn Client<Context = Context<MyActor>>>>

Тем не менее, код все равно не будет работать, потому что Actor имеет границу Self: Sized, что означает, что его нельзя превратить в объект-признак , так что ваша черта, которая его расширяет, не может.1011 *

...