2

I have two traits: Processor and Persist. The first one is responsible for processing some entity, the second one is for asynchronous persisting the processed entity.

Given the two traits I want to implement the trait ProcessPersist taking care of both processing and persisting. Here is how it looks like and compiles fine

use async_trait::*;

trait Processor<T>{
    type Output: Send;  // <----------------------- Note Send here

    fn process(&self, t: T) -> Self::Output;
}

#[async_trait]
trait Persist<T>{
    async fn persist(&self, t: T) -> Result<(), ()>;
}

#[async_trait]
trait ProcessPersist<T>{
    async fn process_persist(&self, t: T) -> Result<(), ()>;
}

#[async_trait]
impl<T, M> ProcessPersist<T> for M
where
    T: Send + 'static,
    M: Processor<T> + Send + Sync,
    M: Persist<<M as Processor<T>>::Output>
{
    async fn process_persist(&self, t: T) -> Result<(), ()> {
        let out = self.process(t);
        self.persist(out).await
    }
}

PROBLEM: The thing that I'm trying to avoid is to remove the context bound type Output: Send because this looks too restrictive. Instead, I'd like it to be specified in the trait ProcessPersist implementation somehow.

Is it possible to do?

Simply removing Send from the type Output does not compile:

use async_trait::*;

trait Processor<T>{
    type Output;  // <----------------------- Send is removed

    fn process(&self, t: T) -> Self::Output;
}

#[async_trait]
trait Persist<T>{
    async fn persist(&self, t: T) -> Result<(), ()>;
}

#[async_trait]
trait ProcessPersist<T>{
    async fn process_persist(&self, t: T) -> Result<(), ()>;
}

#[async_trait]
impl<T, M> ProcessPersist<T> for M
where
    T: Send + 'static,
    M: Processor<T> + Send + Sync,
    M: Persist<<M as Processor<T>>::Output>
{
    async fn process_persist(&self, t: T) -> Result<(), ()> {
        let out = self.process(t);
        self.persist(out).await
    }
}

Error message:

error: future cannot be sent between threads safely
  --> src/main.rs:47:61
   |
47 |       async fn process_persist(&self, t: T) -> Result<(), ()> {
   |  _____________________________________________________________^
48 | |         let out = self.process(t);
49 | |         self.persist(out).await
50 | |     }
   | |_____^ future returned by `__process_persist` is not `Send`
   |
   = help: within `impl core::future::future::Future`, the trait `std::marker::Send` is not implemented for `<M as Processor<T>>::Output`
note: future is not `Send` as this value is used across an await
  --> src/main.rs:49:9
   |
48 |         let out = self.process(t);
   |             --- has type `<M as Processor<T>>::Output` which is not `Send`
49 |         self.persist(out).await
   |         ^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `out` maybe used later
50 |     }
   |     - `out` is later dropped here
   = note: required for the cast to the object type `dyn core::future::future::Future<Output = std::result::Result<(), ()>> + std::marker::Send`
Some Name
  • 8,555
  • 5
  • 27
  • 77
  • 1
    [The duplicate applied here](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8e335befceaed06ff1663af8b1d4fc59): just add `M::Output: Send` to the `where` clause. – trent Sep 16 '20 at 13:27
  • @trentcl Exactly, thanks much. – Some Name Sep 16 '20 at 13:28

0 Answers0