Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest split_at_mut when trying to use multiple non-overlapping mutable slices to the same array #58792

Open
estebank opened this issue Feb 27, 2019 · 1 comment
Labels
A-array Area: `[T; N]` A-borrow-checker Area: The borrow checker A-diagnostics Area: Messages for errors, warnings, and lints A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` C-enhancement Category: An issue proposing an enhancement or a PR with one. P-low Low priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@estebank
Copy link
Contributor

The following code is invalid due to lifetime rules:

fn main() {
    let mut foo = [1,2,3,4];
    let mut a = &mut foo[..2];
    let mut b = &mut foo[2..];
    a[0] = 5;
    b[0] = 6;
    println!("{:?} {:?}", a, b);
}
error[E0499]: cannot borrow `foo` as mutable more than once at a time
 --> src/main.rs:4:22
  |
3 |     let mut a = &mut foo[..2];
  |                      --- first mutable borrow occurs here
4 |     let mut b = &mut foo[2..];
  |                      ^^^ second mutable borrow occurs here
5 |     a[0] = 5;
  |     ---- first borrow later used here

The solution is to either use unsafe or (more appropriately) use split_at_mut:

fn main() {
    let mut foo = [1,2,3,4];
    let (mut a, mut b) = foo.split_at_mut(2);
    a[0] = 5;
    b[0] = 6;
    println!("{:?} {:?}", a, b);
}

The compiler should detect the case of multiple mutable borrows to the same array/slice and hint at the existence of split_at_mut. Ideally, we would also verify wether there's a range overlap in order to explain to the user why what they want to do is problematic and disallowed in Rust.

The output should ideally be along the lines of the following (the structured suggestion is not needed to fix this issue, the text would be enough of an improvement):

error[E0499]: cannot borrow `foo` as mutable more than once at a time
 --> src/main.rs:4:22
  |
3 |     let mut a = &mut foo[..2];
  |                      --- first mutable borrow occurs here
4 |     let mut b = &mut foo[2..];
  |                      ^^^ second mutable borrow occurs here
5 |     a[0] = 5;
  |     ---- first borrow later used here
  = note: you cannot have multiple mutable borrows to the same piece of memory, as the compiler cannot assure that the different slices you're trying to use are non-overlapping
help: you can instead use `split_at_mut` to get two non-overlapping mutable slices from a single array/slice
  |
3 |     let (mut a, mut b) = foo.split_at_mut(2);
  |         ^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^
@estebank estebank added A-diagnostics Area: Messages for errors, warnings, and lints A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` labels Feb 27, 2019
@estebank estebank added D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 8, 2019
@estebank estebank added A-borrow-checker Area: The borrow checker C-enhancement Category: An issue proposing an enhancement or a PR with one. labels Nov 19, 2019
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Feb 4, 2020
Suggest `split_at_mut` on multiple mutable index access

cc rust-lang#58792.
@workingjubilee workingjubilee added the A-array Area: `[T; N]` label Mar 7, 2023
estebank added a commit to estebank/rust that referenced this issue Apr 24, 2024
```
error[E0499]: cannot borrow `foo` as mutable more than once at a time
  --> $DIR/suggest-split-at-mut.rs:13:18
   |
LL |     let a = &mut foo[..2];
   |                  --- first mutable borrow occurs here
LL |     let b = &mut foo[2..];
   |                  ^^^ second mutable borrow occurs here
LL |     a[0] = 5;
   |     ---- first borrow later used here
   |
   = help: use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices
```

Address most of rust-lang#58792.

For follow up work, we should emit a structured suggestion for cases where we can identify the exact `let (a, b) = foo.split_at_mut(2);` call that is needed.
estebank added a commit to estebank/rust that referenced this issue Apr 25, 2024
```
error[E0499]: cannot borrow `foo` as mutable more than once at a time
  --> $DIR/suggest-split-at-mut.rs:13:18
   |
LL |     let a = &mut foo[..2];
   |                  --- first mutable borrow occurs here
LL |     let b = &mut foo[2..];
   |                  ^^^ second mutable borrow occurs here
LL |     a[0] = 5;
   |     ---- first borrow later used here
   |
   = help: use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices
```

Address most of rust-lang#58792.

For follow up work, we should emit a structured suggestion for cases where we can identify the exact `let (a, b) = foo.split_at_mut(2);` call that is needed.
@estebank
Copy link
Contributor Author

After #124313, the only thing left to do would be to provide a structured suggestion, but in order to do so we'd need to have much more confidence that it can be properly applied and that the sub-slice indices do not match, and separately handle the sub-slicing case

    let (a, b) = foo.split_at_mut(2);
    a[0] = 5;
    b[0] = 6;
    println!("{:?} {:?}", a, b);

from the sub-indexing case

    let (a, b) = foo.split_at_mut(3);
    let a = &mut a[2];
    let b = &mut b[0];
    *a = 5;
    *b = 6;
    println!("{:?} {:?}", a, b);

@estebank estebank added P-low Low priority and removed D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. labels Apr 25, 2024
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Apr 25, 2024
Detect borrow error involving sub-slices and suggest `split_at_mut`

```
error[E0499]: cannot borrow `foo` as mutable more than once at a time
  --> $DIR/suggest-split-at-mut.rs:13:18
   |
LL |     let a = &mut foo[..2];
   |                  --- first mutable borrow occurs here
LL |     let b = &mut foo[2..];
   |                  ^^^ second mutable borrow occurs here
LL |     a[0] = 5;
   |     ---- first borrow later used here
   |
   = help: use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices
```

Address most of rust-lang#58792.

For follow up work, we should emit a structured suggestion for cases where we can identify the exact `let (a, b) = foo.split_at_mut(2);` call that is needed.
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Apr 25, 2024
Rollup merge of rust-lang#124313 - estebank:split-at-mut, r=fee1-dead

Detect borrow error involving sub-slices and suggest `split_at_mut`

```
error[E0499]: cannot borrow `foo` as mutable more than once at a time
  --> $DIR/suggest-split-at-mut.rs:13:18
   |
LL |     let a = &mut foo[..2];
   |                  --- first mutable borrow occurs here
LL |     let b = &mut foo[2..];
   |                  ^^^ second mutable borrow occurs here
LL |     a[0] = 5;
   |     ---- first borrow later used here
   |
   = help: use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices
```

Address most of rust-lang#58792.

For follow up work, we should emit a structured suggestion for cases where we can identify the exact `let (a, b) = foo.split_at_mut(2);` call that is needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-array Area: `[T; N]` A-borrow-checker Area: The borrow checker A-diagnostics Area: Messages for errors, warnings, and lints A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` C-enhancement Category: An issue proposing an enhancement or a PR with one. P-low Low priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

2 participants