The trait bound neon::prelude::Finalize` is not satisfied

Now I'm trying to build a nodejs addon for websocket conversation.
This is the connect part of my code:

impl Finalize for WebSocketClient {}

fn connect(mut cx: FunctionContext) -> JsResult<JsBox<Arc<Mutex<WebSocketClient>>>> {
    let url = cx.argument::<JsString>(0)?.value(&mut cx);
    let client = Arc::new(Mutex::new(WebSocketClient::new()));
    
    let rt = Runtime::new().unwrap();
    let client_clone = Arc::clone(&client);

    rt.block_on(async {
        let mut client_guard = client_clone.lock().await;
        client_guard.connect(url);
    });
    
    Ok(cx.boxed(client_clone))
}

And at the last row, I got this error:
the trait bound tokio::sync::Mutex<WebSocketClient>: neon::prelude::Finalize is not satisfied
the following other types implement trait neon::prelude::Finalize:
()
(T0, T1)
(T0, T1, T2)
(T0, T1, T2, T3)
(T0, T1, T2, T3, T4)
(T0, T1, T2, T3, T4, T5)
(T0, T1, T2, T3, T4, T5, T6)
(T0, T1, T2, T3, T4, T5, T6, T7)

I just started Rust a few days ago, so I can't find what to do with this error.
Somebody help me, please.

1 Like

Please format your code with triple backticks, it'll help us to read it and help you :slight_smile:

Thank you for your care and sorry about the bad formatting.
I edited my post.

1 Like

Thanks! I think the issue will be with the Mutex. If you dereference it you might make some progress.

When the error says a trait bound is not satisfied. It means that the type you've called a method of the trait on (.connect()) does not implement that trait (Finalize), and so the method isn't available.

client_guard.connect(url)

This is the problem I think. If you're able to use rust analyzer to hover and see the type of client_guard I think you'll see what type it is.

To dereference it and get at the actual type being guarded, use *:

(*client_guard).connect(url)

The brackets disambiguate what is being dereferenced, in this case the guard, and not the result of calling .connect().

I hope that steers you in the right direction, let us know if not

Sorry, I guess my post didn't clarify where the error occurs.
At the return part of the function, I got this error.
I mean, here:

Ok(cx.boxed(client_clone))
            ^^^^^^^^^^^^

Dereference didn't resolve the problem.

Be sure to post the full error from running cargo build or cargo check in the terminal. Sometimes there is more info in the full error message that has clues.

error[E0277]: the trait bound `tokio::sync::Mutex<WebSocketClient>: neon::prelude::Finalize` is not satisfied
   --> src/lib.rs:111:17
    |
111 |     Ok(cx.boxed(client_clone))
    |           ----- ^^^^^^^^^^^^ the trait `neon::prelude::Finalize` is not implemented for `tokio::sync::Mutex<WebSocketClient>`, which is required by `Arc<tokio::sync::Mutex<WebSocketClient>>: neon::prelude::Finalize`
    |           |
    |           required by a bound introduced by this call
    |
    = help: the following other types implement trait `neon::prelude::Finalize`:
              ()
              (T0, T1)
              (T0, T1, T2)
              (T0, T1, T2, T3)
              (T0, T1, T2, T3, T4)
              (T0, T1, T2, T3, T4, T5)
              (T0, T1, T2, T3, T4, T5, T6)
              (T0, T1, T2, T3, T4, T5, T6, T7)
            and 28 others
    = note: required for `Arc<tokio::sync::Mutex<WebSocketClient>>` to implement `neon::prelude::Finalize`
note: required by a bound in `neon::context::Context::boxed`
   --> /root/.cargo/registry/src/index.crates.io-6f17d22bba15001f/neon-1.0.0/src/context/mod.rs:444:17
    |
444 |     fn boxed<U: Finalize + 'static>(&mut self, v: U) -> Handle<'a, JsBox<U>> {
    |                 ^^^^^^^^ required by this bound in `Context::boxed`

Ah then it looks like the .boxed method is the issue, and it needs it's argument to implement the Finalize trait.

Probably the Arc is dereferenced for you, but the Mutex it contains doesn't implement Finalize.

This might mean that you're simply not supposed to return a Mutex (even wrapped in Arc).

There should be documentation to guide you in what you're supposed to do instead of wrapping in a Mutex, or it could be an issue with neon itself.

Looking at the docs above, I can see that Finalize is implemented for std::sync::Mutex from the std library, but not tokio::sync::Mutex.

This is unfortunate because you can't implement Finalize (an external trait) for tokio::sync::Mutex (an external type) due to this the orphan rule.

I think you will be able to use a newtype as a workaround:

struct FinalizedMutex<T> {
    pub inner: tokio::sync::Mutex<T>,
}

impl<T: Finalize> Finalize for FinalizedMutex<T> {
    fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
        if let Ok(v) = self.inner.into_inner() {
            v.finalize(cx);
        }
    }
}

Which is basically saying, here's my type, that just wraps tokio's Mutex type. If the Mutex holds something that implements Finalize, then my type should implement Finalize by making the thing inside the Mutex do it's finalizing.

I copied that from neon's impl of Finalize for std::sync::Mutex which is linked to by the docs (pro tip!).
Hopefully tokio's Mutex has the same into_inner() method. The compiler will yell at us if it doesn't :smiley:

P.S that got very complicated very quickly, but don't worry, it's a bit of an edge case and you don't really need to fully understand half of what's going on. But hopefully it's interesting, because these kinds of shenanigans can come up fairly often if you write a lot of rust code.

2 Likes

:sparkling_heart: Thanks a lot. You helped me. :sparkling_heart:
Last one more question, pls...
How long will it take to get used to all the unique features of Rust generally?

It's a difficult question and it varies from person to person, but at least 6 months I'd say.

I think you will be productive, but need to search for answers to problems you have for a long time before things really click. People talk about "fighting the borrow checker". I think it's generally a difficult language to learn.

2 Likes