1

I have two Actix actors. MyActor1 defines a generic trait that one of its fields implements. MyActor2 doesn't need to define T and I can't figure out how to call MyActor1::from_registry() from MyActor2 message handlers without knowing what type T maps to.

I've tried variations of this:

let addr: Addr<MyActor1<T>> = MyActor1::from_registry();

This doesn't work because I don't know where/how to define T unless it's also defined on struct MyActor2<T: Thing> and then added to the impl<T> Handler<Msg> for MyActor2<T> where T:....

I also tried this but it doesn't work because Thing doesn't implement Default (because it's a trait):

let addr: Addr<MyActor1<Thing>> = MyActor1::from_registry();

Here's an example I'm using:

Cargo.toml

[package]
name = "actix-example"
version = "0.1.0"
authors = ["me"]
edition = "2018"

[dependencies]
actix = "0.8.1"

main.rs

#![allow(dead_code)]

use actix::prelude::*;

trait Thing {
    fn name(&self) {}
}

#[derive(Default)]
struct One;
impl Thing for One {}

#[derive(Default)]
struct Two;
impl Thing for Two {}

// MyActor1
#[derive(Default)]
struct MyActor1<T: Thing> {
    thing: T,
}

impl<T> Actor for MyActor1<T>
where
    T: Thing + 'static + Default,
{
    type Context = Context<Self>;
}
impl<T> Supervised for MyActor1<T> where T: Thing + 'static + Default {}
impl<T> SystemService for MyActor1<T> where T: Thing + 'static + Default {}
impl<T> Handler<Msg> for MyActor1<T>
where
    T: Thing + 'static + Default,
{
    type Result = ();
    fn handle(&mut self, _msg: Msg, _ctx: &mut Context<Self>) {}
}

// MyActor2
#[derive(Default)]
struct MyActor2;

#[derive(Message)]
struct Msg;
impl Actor for MyActor2 {
    type Context = Context<Self>;
}
impl Supervised for MyActor2 {}
impl SystemService for MyActor2 {}

impl Handler<Msg> for MyActor2 {
    type Result = ();
    fn handle(&mut self, _msg: Msg, _ctx: &mut Context<Self>) {
        let addr = MyActor1::from_registry();
    }
}

fn main() {
    let sys = System::new("test");
    let act1 = MyActor1 {
        thing: One::default(),
    };
    let act2 = MyActor2::default();
    actix::SystemRegistry::set(act1.start());
    actix::SystemRegistry::set(act2.start());
    let _ = sys.run();
}

When running the code, I get this error:

error[E0283]: type annotations required: cannot resolve `_: Thing`
  --> src/main.rs:50:20
   |
50 |         let addr = MyActor1::from_registry();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^
   |
note: required by `MyActor1`
  --> src/main.rs:15:1
   |
15 | struct MyActor1<T: Thing> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^

I know this solves this example:

let addr: Addr<MyActor1<One>> = MyActor1::from_registry();

What would I do if I didn't know what MyActor1<T> was at runtime? For example, maybe I had some code to initialize MyActor1 as MyActor1<Two> at runtime based on some command line argument.

4

1 回答 1

1

TL;DR: MyActor1 is not a type, it's a blueprint.


When you declare struct Foo<T>, Foo is not a type, it's a blueprint for the compiler to create types, or, as computer science would have it, a type constructor.

If you have the blueprints for a house, you cannot open the door of the blueprint and go for a nap in the bedroom: there is no bedroom, there is an idea of what the bedroom will look like once the house is built.

The same thing applies here. You cannot use a type constructor where a type is needed.


You are left with 3 solutions:

  • Make MyActor1 a type, removing the parameter T; for example, it may store T as a Box<Thing>.
  • Make MyActor2 a generic, adding the parameter T.
  • Implement some run-time discovery, for example using a type-map.

Which is the best solution in your case will vastly depend on your use case, and I am loathe to give any advice based on a reduced example.

于 2019-05-21T12:40:44.030 回答