2

I have a programming assignment of creating a bubble sort using Rust. I don't really have much experience in Rust so this is a little bit hard for me:

fn main() {
    println!("Sort numbers ascending");
    let num:[i32; 10] = [4, 65, 2, -31, 0, 99, 2, 83, 782, 1];
    println!("Before: {:?}", num);
    bubble_sort( num);
    println!("After: {:?}\n", num);
}

fn swap( a: &mut i32, b: &mut i32){
    let tmp: i32 = *b;
    *b=*a;
    *a=tmp;
}

fn bubble_sort(mut arr: [i32; 10]) {
    for i in 0..arr.len() {
        for j in 0..arr.len() - 1 - i {
            if arr[j] > arr[j + 1] {
                swap( &mut arr[j], &mut arr[j + 1]);
            }
        }
    }
}

Error:

Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `arr[_]` as mutable more than once at a time
  --> src/main.rs:19:24
   |
19 |                 swap( &mut arr[j], &mut arr[j + 1]);
   |                 ----  -----------  ^^^^^^^^^^^^^^^ second mutable borrow occurs here
   |                 |     |
   |                 |     first mutable borrow occurs here
   |                 first borrow later used by call
   |
   = help: consider using `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices

For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground` due to previous error
Tom Tom
  • 23
  • 2
  • Does this answer your question? [How to get mutable references to two array elements at the same time?](https://stackoverflow.com/questions/30073684/how-to-get-mutable-references-to-two-array-elements-at-the-same-time) – Chayim Friedman Dec 23 '21 at 06:54

1 Answers1

6

The Rust borrow checker is quite sophisticated, but it has limitations. Here, even though it's obvious to a human that there is no mutable aliasing going on, that fact is totally hidden from the borrow checker. It sees &mut arr[j] and &mut arr[j + 1] and concludes: "you're trying to borrow mutably from arr twice".

The error message does indicate the solution:

split_at_mut() is provided for exactly this situation. Under the hood, it uses unsafe to bend the above rule in a way that is still sound and will not cause UB. It takes an index to split at, and returns 2 non-overlapping mutable slices. This is the example from the docs:

let mut v = [1, 0, 3, 0, 5, 6];
let (left, right) = v.split_at_mut(2);
assert_eq!(left, [1, 0]);
assert_eq!(right, [3, 0, 5, 6]);

So in your case, instead of using &mut arr[j] to get a mutable reference to the number, you can do:

let (start, end) = arr.split_at_mut(1);
swap(&mut start[0], &mut end[0]);

P.S. there's also .swap(i, j) for slices :-D

Flavio Vilante
  • 5,131
  • 1
  • 11
  • 15
cameron1024
  • 9,083
  • 2
  • 16
  • 36