Cannot borrow as mutable

I'm trying to abstract REST-clients, like in Best way to replace an async trait. Currently I have the following code, but I always get the cannot borrow as mutable error, either in the setup method, or if I replace Fn with FnMut, in the Client.something method.

Does anyone have a good idea how to convince rust to run this mutable call in a closure?

Thanks...

use futures::Future;
use std::pin::Pin;

// Change Fn(String) to FnMut(String) to move the "cannot borrow as mutable" error
// to the call in the Client structure.
pub type RestCall = Pin<Box<dyn FnMut(String) -> Pin<Box<dyn Future<Output = ()> + 'static>>>>;

struct Client {
    call: RestCall,
}

impl Client {
    fn new(call: RestCall) -> Client {
        Client { call }
    }

    async fn something(&mut self, s: String) {
        (self.call)(s).await;
    }
}

struct Method {
    something: Option<String>,
}

impl Method {
    fn new() -> Method {
        Method { something: None }
    }

    async fn call(&mut self, new_str: String) {
        self.something = Some(new_str)
    }
}

fn setup(m: &'static Method) -> Client {
    Client::new(Box::pin(move |s| Box::pin(m.call(s))))
}

pub async fn start() {
    let m = Box::leak(Box::new(Method::new()));
    let mut cl = setup(m);
    cl.something("hi".to_string()).await;
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow data in a dereference of `Pin<Box<dyn FnMut(String) -> Pin<Box<dyn futures::Future<Output = ()>>>>>` as mutable
  --> src/lib.rs:16:9
   |
16 |         (self.call)(s).await;
   |         ^^^^^^^^^^^ cannot borrow as mutable
   |
   = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<Box<dyn FnMut(String) -> Pin<Box<dyn futures::Future<Output = ()>>>>>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

Only the future should be pinned, not the closure. You probably want something like this:

pub type RestCall = Box<dyn FnMut(String) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send>;

Hmm - if I put that type-definition, it still fails to compile in the same way. The cannot borrow as mutable is either in the setup method or the something call, depending on Fn or FnMut in the type-definition.

Yes, I should probably re-read the book again, but I don't know exactly where :wink:

What is the new error?

Still the same error: cannot borrow as mutable. Either in the setup method, or in the something call. I put the new type and changed Box::pin to Box::new in the following example:

Except if I change the type in the setup method to be &'static mut Method, then I get a new error:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/lib.rs:37:46
   |
37 |     Client::new(Box::new(move |s| Box::pin(m.call(s))))
   |                                              ^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'_` as defined on the body at 37:26...
  --> src/lib.rs:37:26
   |
37 |     Client::new(Box::new(move |s| Box::pin(m.call(s))))
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `m`
  --> src/lib.rs:37:44
   |
37 |     Client::new(Box::new(move |s| Box::pin(m.call(s))))
   |                                            ^
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
  --> src/lib.rs:37:35
   |
37 |     Client::new(Box::new(move |s| Box::pin(m.call(s))))
   |                                   ^^^^^^^^^^^^^^^^^^^
   = note: expected `Pin<Box<(dyn futures::Future<Output = ()> + std::marker::Send + 'static)>>`
              found `Pin<Box<dyn futures::Future<Output = ()> + std::marker::Send>>`

That error is because your RestCall type does not allow the returned future to borrow from the closure, but the use of an async fn imposes this requirement.

I never was a fan of the whole async-party... Anyway, would the following type-definition capture better what I want?

pub type RestCall<'r> = &'r mut Box<dyn FnMut(String) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send>;

I tried that, but I cannot create a closure that matches this definition.

I don't think it is possible when Method is captured by the closure rather than being an argument to the closure. I think you will have to write your own trait or just not use a closure in the first place.

Dang - I started with a trait, but because it's WASM, it needs to be async. And if I add #[async_trait], WASM complains: https://github.com/rustwasm/wasm-bindgen/issues/2409

But if you have a better idea for Best way to replace an async trait, I'd be very happy to hear it :wink:

My other idea was to drive the Node structure with calls like update_worker_queue, then fetch all REST- and WebRTC-requests from this queue, and push the results back to the Node structure. But this also feels quite clumsy.

Can you explain a bit more with what you're trying to do exactly? Why do you need a closure in the first place?

I want to create a small server-application that runs both in a wasm and an i586 environment. The server communicates using REST and WebRTC with other servers. So I want to pass a REST and a WebRTC implementation to my server component. This way I can keep the server implementation the same for the wasm and i586 environment.

The easiest way would be to use a Trait. But that fails, because the trait needs to be async, and wasm doesn't allow that (see https://github.com/rustwasm/wasm-bindgen/issues/2409).

So I came up with the idea of a closure that can be called by the server whenever it wants to send or check for new messages from the REST or WebRTC connections. But the REST and WebRTC components need some mutable state, this is why I'm trying to have this mutable closure.

In go I would simply use the following, and then I could implement the Rest interface once for wasm and once for i586, and then pass it to the Server structure.

type Rest interface {
  Get(path string) string;
}

type Server struct {
  r: Rest
}

func (s *Server) start(){
  s.r.Get("newID")
}

Have you considered using conditional compilation?

I did consider it and might go down that road. But it will put implementation logic in the common part of the code, which is what I wanted to abstract in the first place...

Conditional compilation worked, but then I got a reply for my wasm<->async_trait problem:

https://github.com/rustwasm/wasm-bindgen/issues/2409#issuecomment-754574965

So I'll implement that. I hope it works, this would be a much nicer solution!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.