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 theDeadActorSpawner
through theActorSpawner
trait without knowing about theDead
* types.What you actually did was to attempt to specialize the
spawn
function to return anArc<DeadRef>
. I modified yourActorSpawner
to return a trait-object:Playground example