JonasKruckenberg / tauri-sys

Bindings to the Tauri API for projects using wasm-bindgen
Apache License 2.0
84 stars 21 forks source link

Use `invoke` in async trait, thread safety error #33

Closed emirror-de closed 6 months ago

emirror-de commented 6 months ago

Hi, thanks for this great library!

I was trying to use the invoke function in a trait implementation but received an error regarding thread safety.

Is this behavior intentionally?

See example and error below.

Cargo.toml

[package]
name = "tauri-sys-trait"
version = "0.1.0"
edition = "2021"

[dependencies]
async-trait = "0.1"
anyhow = "1"
serde = { version = "1", features = ["derive"] }

[dependencies.tauri-sys]
git = "https://github.com/JonasKruckenberg/tauri-sys"
branch = "main"
features = [
  "all",
]

main.rs

use async_trait::async_trait;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Model;

#[async_trait]
trait Adapter {
    async fn model_list(&self) -> anyhow::Result<Vec<Model>>;
}

struct TauriAdapter;

#[async_trait]
impl Adapter for TauriAdapter {
    async fn model_list(&self) -> anyhow::Result<Vec<Model>> {
        tauri_sys::tauri::invoke::<_, Vec<Model>>("get_model_list", &())
            .await
            .map_err(|e| anyhow::anyhow!(e))
    }
}

fn main() {
    println!("Hello, world!");
}

Output:

[Running 'cargo lcheck --all-features']
    Checking tauri-sys-trait v0.1.0 (/tmp/tauri-sys-trait)
error: could not compile `tauri-sys-trait` (bin "tauri-sys-trait") due to 3 previous errors
error[E0277]: `Rc<RefCell<wasm_bindgen_futures::Inner>>` cannot be sent between threads safely
   --> src/main.rs:16:62
    |
16  |       async fn model_list(&self) -> anyhow::Result<Vec<Model>> {
    |  ______________________________________________________________^
17  | |         tauri_sys::tauri::invoke::<_, Vec<Model>>("get_model_list", &())
18  | |             .await
19  | |             .map_err(|e| anyhow::anyhow!(e))
20  | |     }
    | |     ^
    | |     |
    | |_____`Rc<RefCell<wasm_bindgen_futures::Inner>>` cannot be sent between threads safely
    |       within this `{async block@src/main.rs:16:62: 20:6}`
    |
    = help: within `{async block@src/main.rs:16:62: 20:6}`, the trait `Send` is not implemented for `Rc<RefCell<wasm_bindgen_futures::Inner>>`
note: required because it appears within the type `JsFuture`
   --> /.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasm-bindgen-futures-0.4.39/src/lib.rs:101:12
    |
101 | pub struct JsFuture {
    |            ^^^^^^^^
    = note: required because it captures the following types: `wasm_bindgen_futures::JsFuture`
note: required because it's used within this `async` fn body
   --> /.cargo/git/checkouts/tauri-sys-af581fbb8e6f4ef1/55fe1d1/src/tauri.rs:95:5
    |
95  |     #[wasm_bindgen(module = "/src/tauri.js")]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: required because it captures the following types: `ControlFlow<Result<Infallible, serde_wasm_bindgen::error::Error>, wasm_bindgen::JsValue>`, `impl Future<Output = Result<wasm_bindgen::JsValue, wasm_bindgen::JsValue>>`
note: required because it's used within this `async` fn body
   --> /.cargo/git/checkouts/tauri-sys-af581fbb8e6f4ef1/55fe1d1/src/tauri.rs:67:97
    |
67  |   pub async fn invoke<A: Serialize, R: DeserializeOwned>(cmd: &str, args: &A) -> crate::Result<R> {
    |  _________________________________________________________________________________________________^
68  | |     let raw = inner::invoke(cmd, serde_wasm_bindgen::to_value(args)?).await?;
69  | |
70  | |     serde_wasm_bindgen::from_value(raw).map_err(Into::into)
71  | | }
    | |_^
    = note: required because it captures the following types: `impl Future<Output = Result<Vec<Model>, tauri_sys::error::Error>>`
note: required because it's used within this `async` block
   --> src/main.rs:16:62
    |
16  |       async fn model_list(&self) -> anyhow::Result<Vec<Model>> {
    |  ______________________________________________________________^
17  | |         tauri_sys::tauri::invoke::<_, Vec<Model>>("get_model_list", &())
18  | |             .await
19  | |             .map_err(|e| anyhow::anyhow!(e))
20  | |     }
    | |_____^
    = note: required for the cast from `Pin<Box<{async block@src/main.rs:16:62: 20:6}>>` to `Pin<Box<dyn Future<Output = Result<Vec<Model>, anyhow::Error>> + Send>>`
    = note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: `*mut u8` cannot be sent between threads safely
   --> src/main.rs:16:62
    |
16  |       async fn model_list(&self) -> anyhow::Result<Vec<Model>> {
    |  ______________________________________________________________^
17  | |         tauri_sys::tauri::invoke::<_, Vec<Model>>("get_model_list", &())
18  | |             .await
19  | |             .map_err(|e| anyhow::anyhow!(e))
20  | |     }
    | |     ^
    | |     |
    | |_____`*mut u8` cannot be sent between threads safely
    |       within this `{async block@src/main.rs:16:62: 20:6}`
    |
    = help: within `{async block@src/main.rs:16:62: 20:6}`, the trait `Send` is not implemented for `*mut u8`
note: required because it appears within the type `PhantomData<*mut u8>`
   --> /.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/marker.rs:809:12
    |
809 | pub struct PhantomData<T: ?Sized>;
    |            ^^^^^^^^^^^
note: required because it appears within the type `JsValue`
   --> /.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasm-bindgen-0.2.89/src/lib.rs:91:12
    |
91  | pub struct JsValue {
    |            ^^^^^^^
note: required because it appears within the type `ControlFlow<Result<Infallible, Error>, JsValue>`
   --> /.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/control_flow.rs:85:10
    |
85  | pub enum ControlFlow<B, C = ()> {
    |          ^^^^^^^^^^^
    = note: required because it captures the following types: `ControlFlow<Result<Infallible, serde_wasm_bindgen::error::Error>, wasm_bindgen::JsValue>`, `impl Future<Output = Result<wasm_bindgen::JsValue, wasm_bindgen::JsValue>>`
note: required because it's used within this `async` fn body
   --> /.cargo/git/checkouts/tauri-sys-af581fbb8e6f4ef1/55fe1d1/src/tauri.rs:67:97
    |
67  |   pub async fn invoke<A: Serialize, R: DeserializeOwned>(cmd: &str, args: &A) -> crate::Result<R> {
    |  _________________________________________________________________________________________________^
68  | |     let raw = inner::invoke(cmd, serde_wasm_bindgen::to_value(args)?).await?;
69  | |
70  | |     serde_wasm_bindgen::from_value(raw).map_err(Into::into)
71  | | }
    | |_^
    = note: required because it captures the following types: `impl Future<Output = Result<Vec<Model>, tauri_sys::error::Error>>`
note: required because it's used within this `async` block
   --> src/main.rs:16:62
    |
16  |       async fn model_list(&self) -> anyhow::Result<Vec<Model>> {
    |  ______________________________________________________________^
17  | |         tauri_sys::tauri::invoke::<_, Vec<Model>>("get_model_list", &())
18  | |             .await
19  | |             .map_err(|e| anyhow::anyhow!(e))
20  | |     }
    | |_____^
    = note: required for the cast from `Pin<Box<{async block@src/main.rs:16:62: 20:6}>>` to `Pin<Box<dyn Future<Output = Result<Vec<Model>, anyhow::Error>> + Send>>`

[Finished running. Exit status: 101]
JonasKruckenberg commented 6 months ago

Yeah so apparently JsFuture (exported by wasm_bindgen_futures) which is the underpinning type of this crate does not implement Send which async_trait imposes on the methods. As WASM (at least right now) is not multithreaded anyway having a Send requirement doesn't really make sense to begin with.

To solve this you can either use the "proper" async trait that's currently in nightly but will be stable with 1.75.0 since that doesn't require the Send bound, or you disable the Send bound in the async_trait macro like shown here https://docs.rs/async-trait/0.1.74/async_trait/#non-threadsafe-futures

emirror-de commented 6 months ago

Ah awesome! Thanks for this detailed explanation!