Search code examples
rustvectorreference

Rust: changing a vector passed by reference


Here is an example of a vector that is passed to a function that adds values and passes the same vector to another function that sorts it.

fn bubble_sort(mut arr: Vec<i32>) {
    let mut swapped = true;
    while swapped {
        swapped = false;
        for i in 0..arr.len()-1 {
            if arr[i] > arr[i + 1] {
                let tmp = arr[i];
                let arr[i] = arr[i + 1];
                let arr[i + 1] = tmp;
                swapped = true;
            }
        }
    }
}

fn populate(n: i32, mut &factors: Vec<i32>) {
    factors.push(2);
    factors.push(2);
    factors.push(n);
    factors.push(41);
    bubble_sort(factors);
}

fn main() {
    let mut factors: Vec<i32> = Vec::new();
    populate(164, &factors);
    println!("Factors {:?}", factors);
}

The compiler has tried its best to help me, to no avail. Can you help? I know there are library sorting methods, the key issue for me is changing the vector values.


Solution

  • Lets go through the errors. First, we have two of the same.

    error: expected a pattern, found an expression
     --> src/main.rs:8:21
      |
    8 |                 let arr[i] = arr[i + 1];
      |                     ^^^^^^ arbitrary expressions are not allowed in patterns
    
    error: expected a pattern, found an expression
     --> src/main.rs:9:21
      |
    9 |                 let arr[i + 1] = tmp;
      |                     ^^^^^^^^^^ arbitrary expressions are not allowed in patterns
    

    let is only used when declaring new variables. If you just want to assign to an existing variable, you don't use let.

    // let arr[i] = arr[i + 1];
    // let arr[i + 1] = tmp;
    arr[i] = arr[i + 1];
    arr[i + 1] = tmp;
    

    I'll skip over the next error and move to the fourth one.

    error[E0308]: mismatched types
      --> src/main.rs:16:21
       |
    16 | fn populate(n: i32, mut &factors: Vec<i32>) {
       |                     ^^^^^^^^^^^^  -------- expected due to this
       |                     |
       |                     expected `Vec<i32>`, found `&_`
       |
       = note: expected struct `Vec<i32>`
               found reference `&_`
    help: to take parameter `factors` by reference, move `&` to the type
       |
    16 - fn populate(n: i32, mut &factors: Vec<i32>) {
    16 + fn populate(n: i32, factors: &Vec<i32>) {
       |
    

    In Rust, parameters follow PATTERN: TYPE syntax. For most parameters, PATTERN is a single name. TYPE fully determines what you pass into the function. You're looking to make a function that takes a reference, so lets just apply the compiler's suggestion, ignoring the mut for now.

    // fn populate(n: i32, mut &factors: Vec<i32>) {
    fn populate(n: i32, factors: &Vec<i32>) {
    

    Trying to compile this again, we get another error.

    error[E0308]: mismatched types
      --> src/main.rs:21:17
       |
    21 |     bubble_sort(factors);
       |     ----------- ^^^^^^^- help: try using a conversion method: `.to_vec()`
       |     |           |
       |     |           expected `Vec<i32>`, found `&Vec<i32>`
       |     arguments to this function are incorrect
       |
       = note: expected struct `Vec<_>`
               found reference `&Vec<_>`
    note: function defined here
      --> src/main.rs:1:4
       |
    1  | fn bubble_sort(mut arr: Vec<i32>) {
       |    ^^^^^^^^^^^ -----------------
    

    Rust's borrowing model has three levels: owned (T), mutable reference (&mut T), and shared reference (&T). You can turn values earlier in the list into values later in the list, but not the other way around.

    Here, your populate function has &Vec<i32>, a shared reference, while bubble_sort wants Vec<i32>, an owned value. The compiler suggests .to_vec(), which makes a copy of the Vec. This would make the code compile, but it won't do what you want, since bubble_sort will be operating on a new, independent value.

    You could upgrade populate to take an owned Vec<i32>, but lets downgrade bubble_sort to take a &Vec<i32> instead.

    // fn bubble_sort(mut arr: Vec<i32>) {
    fn bubble_sort(arr: &Vec<i32>) {
    

    Now when we compile, we get six of the same error, among which there are two suggested fixes.

    error[E0596]: cannot borrow `*arr` as mutable, as it is behind a `&` reference
     --> src/main.rs:8:17
      |
    8 |                 arr[i] = arr[i + 1];
      |                 ^^^ `arr` is a `&` reference, so the data it refers to cannot be borrowed as mutable
      |
    help: consider changing this to be a mutable reference
      |
    1 | fn bubble_sort(arr: &mut Vec<i32>) {
      |                      +++
    
    error[E0596]: cannot borrow `*factors` as mutable, as it is behind a `&` reference
      --> src/main.rs:17:5
       |
    17 |     factors.push(2);
       |     ^^^^^^^ `factors` is a `&` reference, so the data it refers to cannot be borrowed as mutable
       |
    help: consider changing this to be a mutable reference
       |
    16 | fn populate(n: i32, factors: &mut Vec<i32>) {
       |                               +++
    

    These are easy to understand. We want to mutate the Vec so we must borrow it mutably. Apply these two fixes and we are down to one error.

    // fn populate(n: i32, factors: &Vec<i32>) {
    fn populate(n: i32, factors: &mut Vec<i32>) {
    
    // fn bubble_sort(arr: &Vec<i32>) {
    fn bubble_sort(arr: &mut Vec<i32>) {
    

    The last error:

    error[E0308]: mismatched types
      --> src/main.rs:26:19
       |
    26 |     populate(164, &factors);
       |     --------      ^^^^^^^^ types differ in mutability
       |     |
       |     arguments to this function are incorrect
       |
       = note: expected mutable reference `&mut Vec<_>`
                          found reference `&Vec<_>`
    note: function defined here
      --> src/main.rs:16:4
       |
    16 | fn populate(n: i32, factors: &mut Vec<i32>) {
       |    ^^^^^^^^         ----------------------
    

    Remember that we upgraded this parameter from a shared reference to a mutable reference in the function signature, so we need to do the same for the call site.

    // populate(164, &factors);
    populate(164, &mut factors);
    

    And it works! You can try the full code on the playground here.


    One thing that often confuses people is when to put mut before the variable name and when to put it after. That is explained in this post: What's the difference between placing "mut" before a variable name and after the ":"?

    Be sure to read the Book, which explains all these essentials of Rust, including borrowing.