#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
это тоже копия, так что концептуальной разницы нет; проверьте сборку сборок выпуска, чтобы увидеть, какие оптимизации на самом деле происходят на практике. Если нет практической разницы, я бы склонился к приведению указателя, поскольку (я думаю) это немного безопаснее, чем трансмутация.