Как определить аннотацию типа при вызове MyActor::from_registry() из обработчика другого актора, который не определяет T?

У меня есть два актера Actix. MyActor1 определяет общий признак, который реализует одно из его полей. MyActor2 не нужно определять T, и я не могу понять, как вызывать MyActor1::from_registry() из обработчиков сообщений MyActor2, не зная, к какому типу относится T.

Я пробовал варианты этого:

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

Это не работает, потому что я не знаю, где/как определить T, если только оно не определено в struct MyActor2<T: Thing>, а затем добавлено в impl<T> Handler<Msg> for MyActor2<T> where T:....

Я также пробовал это, но это не работает, потому что Thing не реализует Default (потому что это черта):

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

Вот пример, который я использую:

Груз.томл

[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();
}

При запуске кода я получаю эту ошибку:

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> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^

Я знаю, что это решает этот пример:

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

Что бы я делал, если бы не знал, что такое MyActor1<T> во время выполнения? Например, возможно, у меня был код для инициализации MyActor1 как MyActor1<Two> во время выполнения на основе какого-то аргумента командной строки.

Я не понимаю логики вашего вопроса. Вы создали общий актер, а это значит, что может быть несколько актеров с разными конкретными типами (например, MyActor1<i32> и MyActor1<bool>). Если ты не знает, на какого актера вы хотите сослаться, как будет код?

Shepmaster 21.05.2019 04:39

Вы буквально ищете синтаксис let addr = MyActor1::<One>::from_registry()?

Shepmaster 21.05.2019 04:40

Если да, то этот вопрос будет дубликатом Как мне указать тип значения, когда нет параметров или атрибутов типа?.

Shepmaster 21.05.2019 04:40
может быть, у меня был какой-то код для инициализации MyActor1 как MyActor1<Two> во время выполнения — это не то, как работают типизированные языки статически.
Shepmaster 21.05.2019 04:41
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
4
146
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

TL;DR: MyActor1 это не тип, это план.


Когда вы объявляете struct Foo<T>, Foo не является типом, это план для компилятора по созданию типов или, как говорят в компьютерных науках, конструктор типов.

Если у вас есть чертежи дома, вы не можете открыть дверь чертежа и пойти вздремнуть в спальне: спальни нет, есть представление о том, как будет выглядеть спальня, когда дом будет построен.

То же самое применимо и здесь. Вы не можете использовать конструктор типов там, где требуется тип.


У вас осталось 3 решения:

  • Сделать MyActor1 типом, убрав параметр T; например, он может хранить T как Box<Thing>.
  • Сделайте MyActor2 универсальным, добавив параметр T.
  • Реализуйте обнаружение во время выполнения, например, с помощью карты типов.

Какое решение является лучшим в вашем случае, будет в значительной степени зависеть от вашего варианта использования, и я ненавижу давать какие-либо советы, основанные на сокращенном примере.

Другие вопросы по теме