Closure requires unique access to `*caches` but it is already borrowed

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=672cf938afd669267ed648269b32b63e

crossbeam package is used in the demo, ant the demo compiled failed:

error[E0500]: closure requires unique access to `*caches` but it is already borrowed
  --> src/main.rs:45:34
   |
44 |         let cache = caches.get_mut(i);
   |                     ----------------- borrow occurs here
45 |         crossbeam::thread::scope(|s| {
   |                                  ^^^ closure construction occurs here
...
48 |                 cache.set(if i == 0 {
   |                 ----- first borrow later captured here by closure
...
57 |                 let specified_item = caches.get_mut(i-1);
   |                                      ------ second borrow occurs due to use of `*caches` in closure

For more information about this error, try `rustc --explain E0500`.

how can I solve this? Thansk!

You are trying to mutate the cache from multiple
places at the same time. This is not allowed, because it is not memory safe even in single-threaded code.

What are you trying to accomplish at the high level?

Methods that take &mut self borrow the entire struct, regardless of the body of the function. So even though you're only returning a single index of the Vec within, the entire struct remains borrowed so long as that returned reference is in use. This means that if it's not part of the returned value from the method, you cannot

  • Get a reference to the previous element at the same time
  • Look at anything else in the struct either, like caches.count

See also this article on interprocedural conflicts. But note that even view structs wouldn't help you here unless they could split a slice borrow (bypassing the IndexMut implementation) ala slice patterns.

One way to resolve it for this code is to both

  • Look up caches.count before the call to get_mut
    • Which assumes get_mut doesn't change count
  • Return an Option<&mut CacheItem> for the preceding element, in addition to the &mut CacheItem for the specified element

Playground.

Thank you very much for your help, your code is working fine. What if I want the first return value of get_mut to be Vec<&mut CacheItem> or some other collection type which make the overhead minimal?
Because all the values generated in the previous loops may be used in the current loop.

I tried to change it many times, but I was not able to succeed.

If you returned two non-overlapping slices (&mut [CacheItem]) it would work. The key part is not having overlap between the code in the closure and the code outside that closure.

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.