Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mutable vs. immutable borrows in closure?

Tags:

rust

I can't figure out how to get the following to work. I think I need the closure to borrow by &mut Vec, but I don't know how to express that. This is distilled from a larger function, but shows the same error.

fn main() {
    let mut v = vec![0; 10];

    let next = |i| (i + 1) % v.len();

    v[next(1usize)] = 1;

    v.push(13);

    v[next(2usize)] = 1;    
}

Error:

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
 --> a.rs:9:5
  |
5 |     let next = |i| {
  |                --- immutable borrow occurs here
6 |         (i + 1) % v.len()
  |                   - first borrow occurs due to use of `v` in closure
...
9 |     v[next(1usize)] = 1;
  |     ^ ---- immutable borrow later used here
  |     |
  |     mutable borrow occurs here

error: aborting due to previous error
like image 945
Tumbleweed53 Avatar asked May 20 '26 00:05

Tumbleweed53


2 Answers

If you really want to do it with a closure, you will have to pass the vector by parameter:

let next = |v: &Vec<_>, i| (i + 1) % v.len();

This makes the closure borrow per-call, rather than capture for the scope. You still need to separate the borrows, though:

let j = next(&v, 1usize);
v[j] = 1;

To make your life easier, you can put everything inside the closure instead:

let next = |v: &mut Vec<_>, i, x| {
    let j = (i + 1) % v.len();
    v[j] = x;
};

Which allows you to simply do:

next(&mut v, 1usize, 1);
next(&mut v, 2usize, 2);
// etc.

This pattern is useful for cases where you are writing a closure just for avoiding local code repetition (which I suspect is why you are asking given the comments).

like image 198
Acorn Avatar answered May 23 '26 06:05

Acorn


Since the closure only needs the length of the Vec. Then instead, you can just get that prior to the closure. Then you avoid the whole borrowing issue, as the closure doesn't need to borrow v anymore.

fn main() {
    let mut v = vec![0; 10];

    let len = v.len();
    let next = |i| (i + 1) % len;

    v[next(1usize)] = 1;
}

Assuming your closure is not dependent on other things, then instead of a closure, you could define a trait with a method that does that.

For simplicity let's call the trait VecExt and the method set.

trait VecExt<T> {
    fn set(&mut self, index: usize, value: T);
}

impl<T> VecExt<T> for Vec<T> {
    fn set(&mut self, index: usize, value: T) {
        let len = self.len();
        self[(index + 1) % len] = value;
    }
}

fn main() {
    let mut v = vec![0; 10];

    v.set(1, 1);

    v.push(13);

    v.set(2, 1);
}
like image 20
vallentin Avatar answered May 23 '26 06:05

vallentin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!