3

我开始着手我的第一个更雄心勃勃的 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一个错误。
  • 我试图在 trait 的实现 ( impl Simulation) 中指定类型,但得到了associated types are not allowed in inherent impls一个错误。
  • 我尝试了一些东西impl <T: Actor> Simulation for T(例如,如图所示,但没有任何效果。

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

4

2 回答 2

3

只要我从您的代码中理解,您的主要目标就是在集合中添加所有客户端/参与者,并在需要时调用它的常见行为。但由于对象安全,它不可能,所以我们可以通过一些技巧来做到这一点(创建特征模仿客户端,我将其命名为ClientProxy)。

我有一个自己的特征,它定义了第三方集成的接口。我们称它为客户端。我希望每个客户都表现得像个演员。

pub trait Client: Actor {}

是的,它就是这样工作的,实际上这意味着如果某个结构有客户端的实现,它也需要有 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);
    }
}

现在我们有了要测试的 Actor,但让我们回到我们的问题对象安全,我们不能将这些客户端收集到一个集合中。这就是为什么我创建ClientProxy来模仿Client并且我想ClientProxyClients. 我们可以通过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 上运行)

于 2018-12-16T20:43:57.627 回答
1

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

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

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

但是,代码仍然无法工作,因为Actor它有一个边界Self: Sized,这意味着它不能被制作成一个 trait object,所以你的 trait 也不能​​扩展它。

于 2018-12-16T19:07:26.883 回答