svprim
svprim

Reputation: 23

Cannot move out of here move occurs because `slice[_]` has type `T`, which does not implement the `Copy` trait

I am trying to write quick sort in Rust, I got it working when I used the specific type [i32] but I get an error when I try to use [T].

fn main() {
    let mut arr = vec![4,3,2,1];
    quick_sort(&mut arr);
    println!("{:?}", arr);
}

fn partition<T: Ord>(slice: &mut [T]) -> usize {
    let end = slice.len() - 1;
    let pivot = slice[end];
    let mut j = 0;
    for i in 0..end {
        if slice[j] <= pivot {
            slice.swap(i, j);
            j += 1;
        }
    }
    slice.swap(end, j);
    j
}

fn quick_sort<T: Ord>(slice: &mut [T]) {
    if !slice.is_empty() {
        let j = partition(slice);
        let len = slice.len();
        
        quick_sort(&mut slice[0..j]);
        quick_sort(&mut slice[j+1..len]);
    }
}

I get the following error:

error[E0508]: cannot move out of type `[T]`, a non-copy slice
  --> src/main.rs:9:17
   |
  9|     let pivot = slice[end];
   |                 ^^^^^^^^^^ cannot move out of here
   |                            move occurs because `slice[_]` has type `T`, 
   |                            which does not implement the `Copy` trait
   |                            help: consider borrowing here: `&slice[end]`

When let pivot = &slice[end]; , I get a different error:

error[E0308]: mismatched types
  --> src/main.rs:12:22
   |
  7| fn partition<T: Ord>(slice: &mut [T]) -> usize {
   |              - this type parameter
...
 12|        if slice[j] <= pivot {
   |                       ^^^^^ expected type parameter `T`, found `&T`
   = note: expected type parameter `T`
                   found reference `&T`

I cannot get this to work with [T].

Upvotes: 2

Views: 1232

Answers (1)

Dogbert
Dogbert

Reputation: 222348

We can fix the "expected type parameter T, found &T" error by changing the if to:

if &slice[j] <= pivot {

but that runs into another error:

error[E0502]: cannot borrow `*slice` as mutable because it is also borrowed as immutable
  --> src/main.rs:13:13
   |
9  |     let pivot = &slice[end];
   |                 ----------- immutable borrow occurs here
...
12 |         if &slice[j] <= pivot {
   |                         ----- immutable borrow later used here
13 |             slice.swap(i, j);
   |             ^^^^^^^^^^^^^^^^ mutable borrow occurs here

This is because we are taking a reference to a value from the slice and while that reference is alive, we are calling a method that requires mutable access to the slice.

To fix this, we can inline the referencing into the if statement itself:

fn partition<T: Ord>(slice: &mut [T]) -> usize {
    let end = slice.len() - 1;
    let mut j = 0;
    for i in 0..end {
        if slice[j] <= slice[end] {
            slice.swap(i, j);
            j += 1;
        }
    }
    slice.swap(end, j);
    j
}

Output:

[1, 2, 3, 4]

Playground


The reason this worked for [i32] is because calling slice[end] implicitly created a copy of the value because i32 implements the Copy trait. If a type does not implement Copy, you need to either take a reference using &slice[index] or if it implements Clone, call slice[index].clone(). In this code you have a generic T which does not implement either of those.

Upvotes: 2

Related Questions