Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a factory trait that returns generic values with a matching type parameter?

Tags:

rust

Consider the following code (simplified but compilable):

use std::sync::Arc;

pub trait ActorRef<Message> { /* ... */ }
pub trait Actor<Message> { /* ... */ }
pub trait ActorSpawner {
    /// Spawns a new actor returning an actor ref 
    /// for passing messages to it.
    fn spawn<Message,A,R>(actor: A) -> Arc<R>
        where R: ActorRef<Message>, A: Actor<Message>;
}

Is it possible to implement ActorSpawner::spawn or to achieve something similar with another signature?

The idea

The code in the question is simplified to reduce it to the core parts that I couldn't solve.

In general, an Actor should have mutable state which is changed by the processing messages (a process method is missing from the example). You can spawn an Actor and can communicate with it via an ActorRef (the send method is missing from the example).

I want to allow different ways of "spawning" an Actor. E.g. the message processing might happen on one thread per actor. Or the processing might be done on a thread pool which is shared by other actors.

Other code might depend on creating further actors. The underlying mechanism used should be abstracted. Since the trait ActorSpawner.

My attempts to solve it

Let's assume that we have some dummy implementations for Actor and ActorRef:

struct NoopActor;
impl<Message> Actor<Message> for NoopActor {}

struct DeadRef;
impl<Message> ActorRef<Message> for DeadRef {}

It should now be possible to implement the trait somehow using these dummy implementations.

This is my first attempt:

struct DeadActorSpawner;
impl ActorSpawner for DeadActorSpawner {
    fn spawn<Message,A,R>(actor: A) -> Arc<R>
        where R: ActorRef<Message>, A: Actor<Message> 
    {
        Arc::new(DeadRef)
    }
}

resulting in this error:

error[E0308]: mismatched types
  --> src/main.rs:29:18
   |
29 |         Arc::new(DeadRef)
   |                  ^^^^^^^ expected type parameter, found struct `DeadRef`
   |
   = note: expected type `R`
              found type `DeadRef`

Or another one:

struct DeadActorSpawner;
impl ActorSpawner for DeadActorSpawner {
    fn spawn<Message,A,R>(actor: A) -> Arc<DeadRef>
    {
        Arc::new(DeadRef)
    }
}

resulting in this error:

error[E0053]: method `spawn` has an incompatible type for trait
  --> src/main.rs:25:42
   |
12 |     fn spawn<Message, A, R>(actor: A) -> Arc<R>
   |                                          ------ type in trait
...
25 |     fn spawn<Message, A, R>(actor: A) -> Arc<DeadRef> {
   |                                          ^^^^^^^^^^^^ expected type parameter, found struct `DeadRef`
   |
   = note: expected type `fn(A) -> std::sync::Arc<R>`
              found type `fn(A) -> std::sync::Arc<DeadRef>`

I have tried numerous other things to no avail including using associated types for the Message in Actor and ActorRef.

like image 332
Peter Kolloch Avatar asked Jun 10 '15 08:06

Peter Kolloch


1 Answers

Is it possible to implement ActorSpawner::spawn

Yes, but there's no useful implementation as I see it

or to achieve something similar with another signature

I believe you were expecting an Arc<ActorRef<Message>> to be returned. So you could use the DeadActorSpawner through the ActorSpawner trait without knowing about the Dead* types.

What you actually did was to attempt to specialize the spawn function to return an Arc<DeadRef>. I modified your ActorSpawner to return a trait-object:

pub trait ActorSpawner {
    fn spawn<Message, A>(actor: A) -> Arc<ActorRef<Message>> where A: Actor<Message>;
}

struct DeadActorSpawner;
impl ActorSpawner for DeadActorSpawner {
    fn spawn<Message, A>(actor: A) -> Arc<ActorRef<Message>>
        where A: Actor<Message>
    {
        Arc::new(DeadRef)
    }
}

Playground example

like image 159
oli_obk Avatar answered Oct 15 '22 06:10

oli_obk