代码之家  ›  专栏  ›  技术社区  ›  jdno

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

  •  3
  • jdno  · 技术社区  · 6 年前

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

    对于我的项目,我需要与不同的第三方服务进行接口,并且我决定使用 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 (如图所示) here 但没什么效果。

    我的假设是我缺少了一个关于性格和类型的重要知识。如果有人能帮我解决我的问题,并把我指向丢失的拼图块的方向,我将非常感激。我觉得这里有一个关于生锈的重要教训,我真的很想学习。

    2 回复  |  直到 6 年前
        1
  •  1
  •   Peter Hall    6 年前

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

    您可以通过为 Context 关联类型:

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

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

        2
  •  2
  •   Ömer Erden Eugen Dück    6 年前

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

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

    pub trait Client: Actor {}
    

    是的,这是这样工作的,实际上这意味着如果某个结构有一个客户端的实现,那么它也需要有actor的实现。

    我们有两种背景 男演员 其他演员 以及它们的客户机/参与者实现。我们在客户机中有一种行为(表现得像客户机(&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 我想实施 客户端代理 Clients . 我们可以通过实现 客户端代理 关于扩展客户机的泛型:

    //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 (由于缺乏依赖性,它不在操场上运行)