Rust / wasm32 / refcell / double borrow_mut() // how to debug?

  1. No, I do not have a minimal failure case.

  2. I am havking on a large proprietary rust codebase. It is compiled to wasm32.

  3. I get a double borrow_mut() error.

  4. Is there anyway to get the two lines where the borrow_mut() happens? Right now, I have no idea how to begin debugging this.

In how many places is borrow_mut() called? Because you could add the wasm equivalent of a println!() before each one to see which one is being called before you get the error?

1 Like

By luck, I found the bug, but I had 5 borrow_mut(), 17 borrow(), and the real but was not double borrow_mut(), but

borrow_mut() + borrow()

Still, I'm now insanely paranoid anytime I take borrow() or borrow_mut() on an object and then call a member function (instead of directly setting a member).

1 Like

Is there a good resource on "RefCell" design patterns? My current intuition is to:

  1. instead of having a "big" RefCell over an important struct

  2. have lots of "small" RefCell over each member of the structs I want to be able to mutate

This, in an effort to "make refcell less likely to collide due to making it more finegrained" has the unforunate side effect that it increases the number of borrow() / borrow_mut()

1 Like

Not sure if there’s an established RefCell usage pattern, but personally, I try to minimize the number of places I call borrow/borrow_mut. Instead, borrow the interior value in some entry point to an API, and then pass the resulting reference (or the Ref handle, if you must) around rather than having multiple places in a call graph attempt their own borrows. This is essentially the same thing you’d want to do with a mutex or some other non-reentrant synchronization mechanism. Passing references around also encodes (somewhat) the state of the program: if an API is taking a reference, then the “lock” is already held.

Working with RefCell otherwise, such as sprinkling independent borrow calls, will almost always lead to the type of error you’ve observed, given a sufficiently complex call graph.

3 Likes

@vitalyd : Is it correct to approximate your technique as:

a few number of "big" RefCells ?

I’m not sure they necessarily need to be “big”, although that may frequently end up being the case. The cell holds the state you’d like to use interior mutability on - that may be big or small, depends on need. I think the key aspect is to find that API/functionality entrypoint, borrow there, and then pass the reference around the other functions participating in the API/functionality. Essentially, you want to avoid sprinkling borrow calls around APIs that interact.

Also, I try really hard to avoid RefCell entirely - doesn’t always work out, but it’s always worth a shot. It’s possible a RefCell is truly needed, but it can oftentimes be a symptom of suboptimal factoring, so it’s a good opportunity to stop and review the design.

1 Like

My situation is:

  1. I am usings tdwedb.

  2. I am defining Closures (which are attached to event handlers: mouse move, keyboard, resize, etc ...). On these handlers, state needs to be modified. I am doing this by having an Rc to the underlying objects. However, since there are more than one Rc, I end up needing them ot be Rc<RefCell<>> to borrow / mutate.

  3. I don't know if this is good/bad -- in all of Rust so far, I have managed to avoid Rc<RefCell<...>> by rethinking the design, but in this particular case -- event handlers needing pointers to objects to be mutated -- I so far see no other way.

Yeah, GUIs with closures as event handlers can be a case where Rc<RefCell<...>> is easier/practical. I think the jury is still out on how to best design UI frameworks in Rust because the classic approaches (OO widget hierarchies, complex mutable object graphs, etc) don’t jive well with Rust.

Another option might be to use Cell for some types/data that allow for it.

Yet another might be for the event handlers to have a channel/buffer where they send/enqueue a message, indicating what state change needs to happen. If you have an event loop of sorts and deferring state changes to well-known points is workable, you’d apply those state changes in a place that owns the state (so no Rc).

In the past 10 months, has anyone built better tools for debugging "double borrow mut" on wasm32?

I just did a large rewrite, ran into this issue again -- and the only thing I'm getting is a panic!, telling me me there's a double borrow_mut.

If I could get the stack trace of EITHER borrow_mut call, it'd be extremely helpful.

Surely you can get the stack trace of the call that failed using RUST_BACKTRACE=1?

How could we pass this to the wasm environment? I'm not sure if it ever understands any environment variables at all.

  1. I'm loading the webpage from Chrome. The webpage is served via "cargo web start ..."

  2. On panics NOT caused by "double borrow mut", I often get stack traces. They're great.

  3. On "double borrow mut" issues, I don't get any stack traces. (Is this a new feature? Do I need to upgrade rustc?)