future created by async block is not `Send` in Rust API

86 views Asked by At

I have the following error in my Rust API.

error: future cannot be sent between threads safely
   --> src/bin/server/server.rs:115:5
    |
115 | /     {
116 | |         info!("identity_create_post({:?}) - X-Span-ID: {:?}", id_create_request, context.get().0.clone());
117 | |         let did: String = match id::create_identity(format!("./{}.hodl", id_create_request.user_id.unwrap()).as_str(), id_create_request.stronghold_password.unwrap().as_str()).await {
118 | |             Ok(did) => did,
...   |
121 | |         Ok(IdentityCreatePostResponse::TheIdentityHasBeenCreatedSuccessfully(models::IdCreateResponse{did:Some(did)}))
122 | |     }
    | |_____^ future created by async block is not `Send`
    |
    = help: the trait `std::marker::Send` is not implemented for `dyn futures::Future<Output = Result<std::string::String, JwkStorageDocumentError>>`
note: future is not `Send` as it awaits another future which is not `Send`
   --> src/bin/server/id.rs:49:26
    |
49  |       let fragment = match document
    |  __________________________^
50  | |     .generate_method(
51  | |       &storage,
52  | |       KeyType::from_static_str("Ed25519"),
...   |
55  | |       MethodScope::VerificationMethod,
56  | |     ).await {
    | |_____^ await occurs here on type `Pin<Box<dyn futures::Future<Output = Result<std::string::String, JwkStorageDocumentError>>>>`, which is not `Send`
    = note: required for the cast from `Pin<Box<{async block@src/bin/server/server.rs:115:5: 122:6}>>` to `Pin<Box<dyn futures::Future<Output = Result<IdentityCreatePostResponse, ApiError>> + std::marker::Send>>

I think the error es fairly common, but I couldn't extract the solution in the threads I have seen so far. I have a function in my API where I call an inner function.

#[async_trait]
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
{
    /// Create a new identity on the ledger
    async fn identity_create_post(
        &self,
        id_create_request: models::IdCreateRequest,
        context: &C) -> Result<IdentityCreatePostResponse, ApiError>
    {
        info!("identity_create_post({:?}) - X-Span-ID: {:?}", id_create_request, context.get().0.clone());
        let identifier: String = match id::create_identity(format!("./{}.keys", id_create_request.user_id.unwrap()).as_str(), id_create_request.stronghold_password.unwrap().as_str()).await {
            Ok(identifier) => identifier,
            Err(e) => {return Err(ApiError(format!("Error creating the identity: {:?}",e).to_string()))},
        };
        Ok(IdentityCreatePostResponse::TheIdentityHasBeenCreatedSuccessfully(models::IdCreateResponse{did:Some(did)}))
    }
...

The error occurrs in the line with the await as inside the function call there is an await with a future without Send.

The part that causes the error is the following.

let fragment = match document
    .generate_method(
      &storage,
      KeyType::from_static_str("Ed25519"),
      JwsAlgorithm::EdDSA,
      None,
      MethodScope::VerificationMethod,
    ).await {
      Ok(fragment) => fragment,
      Err(_) => panic!(),
    };
println!("-----------------------------\n{:?}",document);
drop(fragment);

The function generate_method is not mine and cannot be changed. How can I change the code shown before to solve the error? I have read that it involves Boxing the result, but I have no clue how to do it.

1

There are 1 answers

1
sandevins On

Probably it's not the best way to solve it but I came up with a solution to this. I hope it helps if someone has a similar problem.

let mut document = Arc::new(Mutex::new(IotaDocument::new(&network_name)));
let mut document_clone = Arc::clone(&document);
let result = tokio::task::spawn_blocking(move || {
  let mut document_clone = document_clone.lock().unwrap();
  futures::executor::block_on(document_clone.generate_method(
      &storage,
      KeyType::from_static_str("Ed25519"),
      JwsAlgorithm::EdDSA,
      None,
      MethodScope::VerificationMethod,
  ))
}).await??;
let document_clone = document.lock().unwrap().clone();
drop(document);