在继承自另一个特征的特征中指定关联类型

Specifying associated type in trait that inherits from another trait

我开始着手我的第一个更雄心勃勃的 Rust 项目,并且在我用于学习的任何资源和教程中都没有遇到过一些东西。问题的标题抓住了抽象问题,但对于示例,我将使用我正在与之抗争的具体示例。

对于我的项目,我需要与不同的 third-party 服务进行交互,因此我决定使用 actix 框架作为我领域中不同参与者的抽象。该框架定义了必须实现的 Actor 特性:

use actix::prelude::*;

struct MyActor {
    count: usize,
}

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

我有自己的特点,它定义了 third-party 集成的接口。我们称它为 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

我现在花了几个小时试图解决这个问题,但 none 成功了。

我的假设是我缺少关于特征和类型的重要知识。如果有人能帮助我解决我的问题,并指出我丢失的拼图的方向,我将不胜感激。我觉得这里有一个关于 Rust 的重要课程,我真的很想学习。

集合中所有项目的所有方法的签名必须相同,以便您可以互换使用它们。这意味着每个项目的关联类型也必须相同。

您可以通过为 Context 关联类型提供具体类型来消除此错误:

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

但是,代码仍然无法工作,因为 Actor 有一个 Self: Sized 的界限,这意味着 it can't be made into a trait object,所以你的特征也不能扩展它。

只要我从你的代码中了解到,你的主要目标是将所有 Client/Actors 添加到一个集合中,并在它的 needed.But 由于对象安全而不可能时调用它的常见行为所以我们可以做它有一点技巧(创建模仿客户端的特征,我将其命名为 ClientProxy)。

I have a trait of my own that defines the interface for the third-party integrations. Let's call it Client. I want each client to behave like an actor.

pub trait Client: Actor {}

是的,它是这样工作的,实际上这意味着如果某个结构有 Client 的实现,它也需要有 Actor 的实现。

假设我们有两个 Context MyActorOtherActor 及其 Client/Actor 实现。我们在客户端中有一个行为 ( behave_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);
    }
}

现在我们有了用于测试的 Actors,但让我们回到我们的对象安全问题,我们无法将这些客户端收集到一个集合中。这就是为什么我创建 ClientProxy 来模仿 Client 并且我想在 Clients 上实现 ClientProxy。我们可以通过在扩展 Clients:

的泛型上实现 ClientProxy 来做到这一点
//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
}

您可以从 playground 获得完整代码(由于缺乏依赖性,它不是 运行 在 playground 上)