[Solved] Unable to pass mutable array to a function inside a for loop


#1

So I have the following code,

fn main() {
    let mut arr: [i32; 7] = [4, 22, 11, 56, 13, 19, 24];

    for (i, elem) in arr.iter_mut().enumerate() {
      if i == 0 {
        continue;
      }
      sort_from_right(&mut arr, i, *elem);
    }
}

fn sort_from_right (arr: &mut [i32], right_index: usize, value: i32) -> &mut [i32] {
   return arr;
}

Which gives me the following error -

   Compiling insertion_sort v0.1.0 (file:///home/abijeet/Projects/rust/insertion_sort)
error[E0499]: cannot borrow `arr` as mutable more than once at a time
 --> src/main.rs:8:28
  |
4 |     for (i, elem) in arr.iter_mut().enumerate() {
  |                      --- first mutable borrow occurs here
...
8 |       sort_from_right(&mut arr, i, *elem);
  |                            ^^^ second mutable borrow occurs here
9 |     }
  |     - first borrow ends here

error: aborting due to previous error

That makes sense based on the borrowing rules, but just wondering, how would I refactor this code?

I could keep all the code that I want to write in the sort_from_right function in place in the for loop itself, but I’d like a cleaner solution.


#2

The simplest solution is to use an indexed loop:

for i in 0 .. arr.len() {
    let elem = arr[i];
    ...
}

#3

If you’re planning to send off a part of a mutable slice to a function, then check out split_at_mut()


#4

So I refactored my code to this,

fn main() {
    let mut arr: [i32; 7] = [4, 22, 11, 56, 13, 19, 24];

    for i in 1 .. arr.len() {
      let elem = arr[i];
      sort_from_right(&mut arr, i, elem);
    }
}

fn sort_from_right (arr: &mut [i32], right_index: usize, value: i32) -> &mut [i32] {
   // ....
}

And it worked, not sure why though. Could you point me to some that documentation explains it?

Thanks.


#5

This case is fine, because let elem = arr[i]; creates a new copy of elem, so the arr is not used any more, and the next line can get an exclusive mutable reference.

Previous example was different, because the iterator arr.iter_mut() locked arr for exclusive use for itself for the entire loop.


#6

BTW, note that passing an array and an index may be wrong in Rust. If you want a function to operate starting from i-th element of an array, it’s better to use a (sub)slice of the array &arr[i..] or arr.split_at_mut(i).


#7

And would I be able to make modifications to the slice which would then affect the original array?

I do agree with you that it makes more sense to send only the slice that we want to use, but then this piece of code does not produce the expected results.

Its supposed to use insertion sort to sort the array,

fn main() {
    let mut arr: [i32; 7] = [4, 22, 11, 56, 13, 19, 24];

    print_array_with_msg("Before sorting: ".to_string(), &arr);

    for i in 1 .. arr.len() {
      let elem = arr[i];
      sort_from_right(&mut arr[0 .. i], elem);
    }
    print_array_with_msg("After sorting: ".to_string(), &arr);
}

fn sort_from_right (arr: &mut [i32], value: i32) -> &mut [i32] {
  if arr.len() <= 1 {
    return arr;
  }

  let mut i = arr.len() - 2;
  while i != 0 && arr[i] > value {
    // move elements over
    arr[i + 1] = arr[i];
    arr[i] = value;
    i -= 1;
  }
  return arr;
}

fn print_array_with_msg (msg_to_print: String, arr: &[i32]) {
   if arr.len() == 0 {
     return;
   }
   print!("{}", msg_to_print);
   let mut arr_elements: String = String::from("");
   for element in arr.iter() {
     arr_elements.push_str(" ");
     arr_elements.push_str(element.to_string().as_ref());
   }

   println!("{}", arr_elements.trim());
}

Output:

Before sorting: 4 22 11 56 13 19 24
After sorting: 4 22 11 19 24 56 24

The output is not making sense to me.


#8

Arrays passed by reference (slices) share the same memory, so changes made in the function are visible outside the function.
In general, Rust never makes copies of potentially large objects like vectors or arrays without an explicit call to .clone() or such.

When you make a slice from a range, like let sub = &arr[10..]; then indexing of that slice starts again from 0, so sub[0] = 1 makes arr[10] == 1.


#9

I think your implementation is just slightly off :slight_smile:. I’m on mobile so here’s a quick n dirty rendition along your lines: Playground


#10

@vitalyd and @kornel - Thanks for your time and help. I understand my mistakes. I’ll go and read the The Rust Programming Language some more. Any other links you think might be helpful to get started with Rust for a JS / PHP programmer?