4

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

6
  • 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) Commented May 26, 2021 at 11:10
  • 1
    Thanks, that's already a nice solution. Commented May 26, 2021 at 11:26
  • 1
    What about via a pointer cast? Commented May 26, 2021 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.. Commented May 27, 2021 at 5: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. Commented May 27, 2021 at 8:58

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.