Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

rust - What does "cannot borrow as immutable because it is also borrowed as mutable" mean in an nested array index?

What does the error mean in this case:

fn main() {
    let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
    v[v[1]] = 999;
}
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
 --> src/main.rs:3:7
  |
3 |     v[v[1]] = 999;
  |     --^----
  |     | |
  |     | immutable borrow occurs here
  |     mutable borrow occurs here
  |     mutable borrow later used here

I found that indexing is implemented via the Index and IndexMut traits and that v[1] is syntactic sugar for *v.index(1). Equipped with this knowledge, I tried to run the following code:

use std::ops::{Index, IndexMut};

fn main() {
    let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
    *v.index_mut(*v.index(1)) = 999;
}

To my surprise, this works flawlessly! Why doesn't the first snippet work, but the second one does? The way I understand the documentation, they should be equivalent, but this obviously isn't the case.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The desugared version is slightly different than what you have. The line

v[v[1]] = 999;

actually desugars to

*IndexMut::index_mut(&mut v, *Index::index(&v, 1)) = 999;

This results in the same error message, but the annotations give a hint as to what's happening:

error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
 --> src/main.rs:7:48
  |
7 |     *IndexMut::index_mut(&mut v, *Index::index(&v, 1)) = 999;
  |      ------------------- ------                ^^ immutable borrow occurs here
  |      |                   |
  |      |                   mutable borrow occurs here
  |      mutable borrow later used by call

The important difference to your desugared version is the evaluation order. The arguments of a function call are evaluated left to right in the order listed, before actually making the function call. In this case this means that first &mut v is evaluated, mutably borrowing v. Next, Index::index(&v, 1) should be evaluated, but this is not possible – v is already mutably borrowed. Finally, the compiler shows that the mutable reference is still needed for the function call to index_mut(), so the mutable reference is still alive when the shared reference is attempted.

The version that actually compiles has a slightly different evaluation order.

*v.index_mut(*v.index(1)) = 999;

First, the function arguments to the method calls are evaluated left to right, i.e. *v.index(1) is evaluated first. This results in a usize, and the temporary shared borrow of v can be released again. Then, the receiver of index_mut() is evaluated, i.e. v is mutably borrowed. This works fine, since the shared borrow has already been finalised, and the whole expression passes the borrow checker.

Note that the version that compiles only does so since the introduction of "non-lexical lifetimes". In earlier versions of Rust, the shared borrow would live until the end of the expression and result in a similar error.

The cleanest solution in my opinion is to use a temporary variable:

let i = v[1];
v[i] = 999;

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...