*mut (dyn std::ops::Fn() + 'static)` нельзя безопасно использовать между потоками с помощью структуры serde::de::DeserializeOwned

Я пытаюсь использовать Iced (фреймворк пользовательского интерфейса на основе архитектуры Elm) с Reqwest (обертка над гипер), который может использовать Serde для десериализации JSON.

Независимо они работают правильно, но я новичок в Rust и что-то в моей реализации не так.

Я начинаю с сетевой функции (в процессе).

#[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)
}

и я пытаюсь использовать его как часть Iced:

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

и я получаю ошибку компиляции:

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)`

Я полагаю, что проблема связана с использованием T: DeserializeOwned и времени жизни в post, при этом право собственности является таким что тип T в async fn post может находиться в потоке, отличном от асинхронного вызова в Command (отсюда и упоминание Send.

Ответ может быть даже в пожизненной ссылке, но у меня еще недостаточно знаний, чтобы увидеть его или понять, правильно ли я думаю. Я вернулся к использованию конкретного типа вместо T, который работает.

Я хотел бы понять, почему эта проблема существует и что я могу сделать, чтобы решить ее.

Заранее спасибо!


person GoodEgg    schedule 08.03.2020    source источник
comment
Это может вам помочь: stackoverflow.com/questions/48017290/   -  person Alan Darmasaputra    schedule 08.03.2020


Ответы (1)


Этот намек:

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

заставляет меня думать, что вся эта проблема связана с тем, что вы используете format! в fn post (см. https://github.com/rust-lang/rust/issues/64960). Попробуйте обновить компилятор ржавчины или переместить URL-адрес перед длинной цепочкой .await:

let url = format!("{}{}", request.base, request.path);
let response = Client::new()
    .post(&url)
    // ...
person jplatte    schedule 07.05.2020