Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
272 views
in Technique[技术] by (71.8m points)

asynchronous - How to restrict async_trait function's return value to implement Sync?

I'm using the async_trait crate to define an async method on a trait. I intend to transform the Future it returns into a Stream, wrap the Stream in an Arc and send the Arc over to multiple threads. Minimal reproducible code is as follows:

use async_trait::async_trait;
use futures::stream::{unfold, Stream};
use std::sync::Arc;
use tokio::runtime::Runtime;

#[async_trait]
trait Trait: Send + Sync {
    async fn do_something(&self) -> i32;
}

async fn use_trait<T: Trait>(x: &T) {
    let boxed: Arc<Box<dyn Stream<Item = i32> + Send + Sync>>;
    let lazy_poller = unfold(None, move |state| async move {
        if let Some(value) = state {
            Some((value, Some(value)))
        } else {
            let value = x.do_something().await;
            Some((value, Some(value)))
        }
    });
    boxed = Arc::new(Box::new(lazy_poller));
    let boxed_clone = boxed.clone();
    let rt = Runtime::new().unwrap();
    rt.block_on(async {
        let _moved = boxed_clone;
        // Do something with `_moved.next()`
    });
}

However it compiled with the following error:

error: future cannot be shared between threads safely
  --> src/main.rs:21:22
   |
21 |     boxed = Arc::new(Box::new(lazy_poller));
   |                      ^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Sync`
   |
   = help: the trait `Sync` is not implemented for `dyn futures::Future<Output = i32> + std::marker::Send`
note: future is not `Sync` as it awaits another future which is not `Sync`
  --> src/main.rs:17:25
   |
17 |             let value = x.do_something().await;
   |                         ^^^^^^^^^^^^^^^^ await occurs here on type `Pin<Box<dyn futures::Future<Output = i32> + std::marker::Send>>`, which is not `Sync`
   = note: required for the cast to the object type `dyn Stream<Item = i32> + Sync + std::marker::Send`

It seems that async_trait desugars the async methods' return types as Pin<Box<dyn Future<...> + Send>>, without specifying Sync. However I thought it would also be fairly common to require Futures to be Sync, in addition to Send.

My questions are:

  • How should I specify the async method's return type to be Sync, and
  • Why does async_trait not specify Sync for the return types automatically?
question from:https://stackoverflow.com/questions/65916163/how-to-restrict-async-trait-functions-return-value-to-implement-sync

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

As already mentioned in the comments, Futures do not need to be Sync for most of the use cases; and when it may be polled from different tasks, FutureExt::shared is the way to go, which is exactly what I needed for my use case. I did not have to transform it into a Stream.

As for my example, it would be:

use async_trait::async_trait;
use futures::future::FutureExt;
use tokio::runtime::Runtime;

#[async_trait]
trait Trait: Send + Sync {
    async fn do_something(&self) -> i32;
}

async fn use_trait<T: Trait>(x: &T) {
    let shared = x.do_something().shared();
    let shared_clone = shared.clone();
    let rt = Runtime::new().unwrap();
    rt.block_on(async {
        let _moved = shared_clone;
        // Do something with `_moved.await`
    });
    println!("{}", shared.await)
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...