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.
Yes, but there's no useful implementation as I see it
I believe you were expecting an
Arc<ActorRef<Message>>to be returned. So you could use theDeadActorSpawnerthrough theActorSpawnertrait without knowing about theDead* types.What you actually did was to attempt to specialize the
spawnfunction to return anArc<DeadRef>. I modified yourActorSpawnerto return a trait-object:Playground example