3

I'm trying to initialize an array of arrays from an iterator using const generics. The following code serves as an example:

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) }
}

However, I'm getting the error:

error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
  --> src\main.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)

Both types clearly have a fixed size, so i'm not understanding this error...

If I use this code with specific values in the program it works:

    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]]

Is this some bug with const generics or am I doing something wrong??

Adam
  • 743
  • 1
  • 6
  • 11
  • 5
    Rather than a bug, it looks more like something which isn't yet implemented. You can use transmute_copy instead, as it doesn't check the size. (not writing this as an answer as there might be a less unsafe solution) – Denys Séguret May 26 '21 at 11:10
  • 1
    Thanks, that's already a nice solution. – Adam May 26 '21 at 11:26
  • 1
    What about via [a pointer cast](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=cc3cc8eb5892880999b155d968c9d75e)? – eggyal May 26 '21 at 15:33
  • That works too. I'm wondering which version is more efficient? , since transmute does memcpy isn't it a waste?, It feels like I'm initializing the array twice.. – Adam May 27 '21 at 05:28
  • `*ptr` is a copy too, so there's no conceptual difference; check the assembly of release builds to see what optimisations actually happen in practice. If there's no practical difference, I'd lean towards the pointer cast as (I think) it's slightly more safe than a transmute. – eggyal May 27 '21 at 08:58
  • Currently, these are the implementations of transmute I can find: `core::mem::transmute`, `generic_array::transmute`, `memoffset::__priv::mem::transmute`, and `nom::lib::std::mem::transmute`. Also, `transmute` is considered safer than `transmute_copy` and the functionality of `transmute` should only be used when absolutely necessary. – Jishan Shaikh Apr 28 '23 at 06:59

0 Answers0