Указание связанного типа в признаке, который наследуется от другого признака
Я начал работать над своим первым более амбициозным проектом 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 ответа
Подписи для всех методов всех элементов в коллекции должны быть идентичными, чтобы вы могли использовать их взаимозаменяемо. Это означает, что связанные типы каждого элемента тоже должны быть одинаковыми.
Вы можете избавиться от этой ошибки, предоставив конкретный тип для Context
связанный тип:
Vec<Box<dyn Client<Context = Context<MyActor>>>>
Тем не менее, код по-прежнему не будет работать, потому что Actor
имеет границы Self: Sized
Это означает, что он не может быть превращен в объект черты, так же как и ваша черта, которая его расширяет.
Как я понимаю из вашего кода, ваша главная цель - добавить всех клиентов / актеров в коллекцию и вызывать обычное поведение, когда это необходимо. Но из-за безопасности объектов это невозможно, поэтому мы можем сделать это с небольшим взломом (создание черты, которая Клиент имитирует, я назвал его как ClientProxy).
У меня есть своя особенность, которая определяет интерфейс для сторонних интеграций. Давайте назовем это Клиентом. Я хочу, чтобы каждый клиент вел себя как актер.
pub trait Client: Actor {}
Да, это работает так, на самом деле это означает, что если какая-то структура имеет реализацию Client, то она также должна иметь реализацию Actor.
Предположим, у нас есть два контекста MyActor и OtherActor и их реализации Client/Actor. И у нас есть поведение в клиенте (effume_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);
}
}
Теперь у нас есть актеры для тестирования, но давайте вернемся к нашей проблеме Object Safety, мы не можем собрать этих клиентов в одну коллекцию. Вот почему я создал 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
}
Вы можете получить полный код с детской площадки (он не работает на детской площадке из-за отсутствия зависимости)