asynchronousrustwebassemblymutability

How do you mutate data in Rust Wasm? Getting borrow checker errors


I've tried everything from Rc to Arc and Mutex async_std which wouldn't even compile despite it apparently including wasm support. I've been fighting with this error for a few days now and I can't fathom the problem when non-wasm seems to work fine and Promise no different. Tried it on not-self as well, using struct-less code, same problem.

use std::cell::RefCell;
use std::rc::Rc;

use wasm_bindgen_futures::spawn_local;

use wasm_bindgen::prelude::*;

pub struct MyClass {
    value: u32,
}

impl MyClass {
    pub fn new() -> Self {
        return Self { value: 0 };
    }

    pub async fn boot_async(this: Rc<RefCell<&mut Self>>) {
        this.borrow_mut().value += 1;
    }

    pub fn boot(&mut self) {
        let this = Rc::new(RefCell::new(self));
        let async_function = MyClass::boot_async(this.clone());
        spawn_local(async_function);
    }
}

#[wasm_bindgen(start)]
fn start() {
    let mut test = MyClass::new();
    test.boot();
}

And I get:

error[E0521]: borrowed data escapes outside of method
  --> src/main.rs:29:30
   |
27 |     pub fn boot(&mut self) {
   |                 ---------
   |                 |
   |                 `self` is a reference that is only valid in the method body
   |                 let's call the lifetime of this reference `'1`
28 |         let this = Rc::new(RefCell::new(self));
29 |         let async_function = MyClass::boot_async(this.clone());
   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                              |
   |                              `self` escapes the method body here
   |                              argument requires that `'1` must outlive `'static`
   |
   = note: requirement occurs because of the type `RefCell<&mut MyClass>`, which makes the generic argument `&mut MyClass` invariant
   = note: the struct `RefCell<T>` is invariant over the parameter `T`
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

Solution

  • Your were close. The problem is that Rc doesn't help since you still have a reference. You need to get rid of it:

    impl MyClass {
        pub fn new() -> Self {
            return Self { value: 0 };
        }
    
        pub async fn boot_async(this: Rc<RefCell<Self>>) {
            this.borrow_mut().value += 1;
        }
    
        pub fn boot(self) {
            let this = Rc::new(RefCell::new(self));
            let async_function = MyClass::boot_async(this.clone());
            spawn_local(async_function);
        }
    }