Emil Sahlén
Emil Sahlén

Reputation: 2130

cannot move out of type `[T; D]`, a non-copy array

I'm initializing an array using code called through FFI and thus I'm using an array if MaybeUninit to represent that data. It looks something like this:

use std::mem::MaybeUninit;

#[allow(unused)]
fn init_with_ffi<T>(data: &mut [T]) {
    todo!();
}

fn get_array<T, const D: usize>() -> [T; D] {
    let mut my_array: [MaybeUninit<T>; D] = unsafe {
        MaybeUninit::uninit().assume_init()
    };
    init_with_ffi(&mut my_array[..]);
    unsafe {
        *(&mut my_array as *mut [MaybeUninit<T>; D] as *mut [T; D])
    }
}

This is similar to one of the examples presented in the docs for MaybeUninit but generic instead. I'm having to use raw pointers here since std::mem::transmute does not work with arrays of a generic size.

However, I'm getting the following error:

error[E0508]: cannot move out of type `[T; D]`, a non-copy array
  --> src/main.rs:14:9
   |
14 |         *(&mut my_array as *mut [MaybeUninit<T>; D] as *mut [T; D])
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |         |
   |         cannot move out of here
   |         move occurs because value has type `[T; D]`, which does not implement the `Copy` trait

This error message makes no sense to me. It would make sense if I was trying to copy a non-copy array or if I was trying to move ONE of the values out of a non-copy array. But I'm trying to move the entire array and my impression has always been that this should be possible, no?

Upvotes: 0

Views: 250

Answers (1)

Colonel Thirty Two
Colonel Thirty Two

Reputation: 26599

Your code is effectively the same as:

let ptr: *mut [T;D] = &mut my_array as *mut [MaybeUninit<T>; D] as *mut [T; D];
*ptr

Which would attempt to copy, not move, the data out of the pointer - hence your issue

The usual solution, demonstrated in the Initializing an array element-by-element section in the MaybeUninit docs, is using std::mem::transmute instead, but currently it does not work with const generics.

Instead, you can use read to copy the elements from the MaybeUninit<T> array to a new T array. In the general case, you would have to worry about duplicating a T and dropping the same contents twice, but since MaybeUninit doesn't call drop on its containing data when it itself is dropped, it should be safe. Otherwise you can use std::mem::forget on the original array to force not calling the drop method.

Upvotes: 1

Related Questions