Инициализировать массив массивов из итератора

#arrays #rust #initialization #const-generics

Вопрос:

Я пытаюсь инициализировать массив массивов с помощью итератора, используя обобщения const. В качестве примера можно привести следующий код:

 pub fn foo<const R: usize, const C: usize>(iter: Iter<i32>) -> [[i32; C]; R] {
    let mut res: [[MaybeUninit<i32>; C]; R] = unsafe { MaybeUninit::uninit().assume_init() };
    let mut counter = 0;
    res.iter_mut().flatten().zip(iter).for_each(|(r, l)| {
        *r = MaybeUninit::new(*l);
        counter  = 1;
    });
    assert_eq!(counter, R * C);
    unsafe { transmute::<_, [[i32; C]; R]>(res) }
}
 

Тем не менее, я получаю ошибку:

 error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
  --> srcmain.rs:14:14
   |
14 |     unsafe { transmute::<_, [[i32; C]; R]>(res) }
   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: source type: `[[MaybeUninit<i32>; C]; R]` (this type does not have a fixed size)
   = note: target type: `[[i32; C]; R]` (this type does not have a fixed size)
 

Оба типа явно имеют фиксированный размер, поэтому я не понимаю этой ошибки…

Если я использую этот код с определенными значениями в программе, он работает:

     let iter = (0..4);

    let res = {
        let mut res: [[MaybeUninit<i32>; 2]; 2] = unsafe { MaybeUninit::uninit().assume_init() };
        res.iter_mut().flatten().zip(iter).for_each(|(r, l)| {
            *r = MaybeUninit::new(l);
        });
        unsafe { transmute::<_, [[i32; 2]; 2]>(res) }
    };

    println!("{:?}", res);   //[[0, 1], [2, 3]]
 

Это какая-то ошибка с генераторами const или я делаю что-то не так??

Комментарии:

1. Вместо ошибки это больше похоже на то, что еще не реализовано. Вместо этого вы можете использовать transmute_copy, так как он не проверяет размер. (не пишу это в качестве ответа, так как может быть менее опасное решение)

2. Спасибо, это уже хорошее решение.

3. Как насчет приведения указателя ?

4. Это тоже работает. Мне интересно , какая версия более эффективна?, так как transmute делает memcpy, разве это не пустая трата времени?, Такое ощущение, что я инициализирую массив дважды..

5. *ptr это тоже копия, так что концептуальной разницы нет; проверьте сборку сборок выпуска, чтобы увидеть, какие оптимизации на самом деле происходят на практике. Если нет практической разницы, я бы склонился к приведению указателя, поскольку (я думаю) это немного безопаснее, чем трансмутация.