2

I'm attempting to use Iced (UI framework based on The Elm Architecture) with Reqwest (wrapper over hyper) which can use Serde for JSON deserialisation.

Independently they work correctly, but I'm new to Rust and something about my implementation is wrong.

I start with an (in-progress) networking function.

#[derive(Deserialize, Debug, Clone)]
pub(crate) enum Error {
    APIError,
    ParseError,
}

impl From<reqwest::Error> for Error {
    fn from(error: reqwest::Error) -> Self {
        Error::APIError
    }
}

pub(crate) async fn post<T>(request: Request) -> Result<T, Error>
where
    T: DeserializeOwned + Debug + Clone,
{
    let headers = standard_headers(request.params);
    let response = Client::new()
        .post(&format!("{}{}", request.base, request.path))
        .json(&request.body)
        .headers(headers)
        .send()
        .await?
        .json::<T>()
        .await?;

    Ok(response)
}

and I attempt to use it as part of Iced:

fn new() -> (Runner, Command<Message>) {
    (
        Runner::Loading,
        Command::perform(post(Login::request()), Message::Next),
    )
}

and I receive the compile error of:

error[E0277]: `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely
  --> src/feature/runner/runner.rs:42:13
   |
42 |             Command::perform(post(Login::request()), Message::Next),
   |             ^^^^^^^^^^^^^^^^ `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely
   | 
  ::: <snip>/futures/src/command/native.rs:29:22
   |
29 |         future: impl Future<Output = T> + 'static + Send,
   |                      ------------------ required by this bound in `iced_futures::command::native::Command::<T>::perform`
   |
   = help: within `core::fmt::Void`, the trait `std::marker::Sync` is not implemented for `*mut (dyn std::ops::Fn() + 'static)`

I believe the problem relates to using T: DeserializeOwned and life times in the post, whereby the ownership is such that the T type in the async fn post might be on a different thread to an async call within Command (hence the mention of Send.

The answer may even be in the lifetime link, but I don't have enough knowledge yet to see it or to know if i'm in the right place with my thinking. I debugged back to just using a concrete type instead of T which works.

I'd love to understand why this problem exists and what I can do to solve it.

Thanks in advance!

GoodEgg
  • 43
  • 5

1 Answers1

0

This hint:

help: within `core::fmt::Void`, the trait `std::marker::Sync` is not implemented for `*mut (dyn std::ops::Fn() + 'static)`

makes me think that this whole issue is related to your use of format! within fn post (see https://github.com/rust-lang/rust/issues/64960). Try upgrading your rust compiler or moving the url out before the long .await chain:

let url = format!("{}{}", request.base, request.path);
let response = Client::new()
    .post(&url)
    // ...
jplatte
  • 1,121
  • 11
  • 21