Transmute in the context of constant generics

Hi. I am trying to get this code work:

use std::mem::MaybeUninit;
use std::mem;

trait ArrayExt {
	type Elem;
	fn fill_with(gen: impl FnMut() -> Self::Elem) -> Self;
}

impl<T, const N: usize> ArrayExt for [T; N] {
	type Elem = T;
	fn fill_with(mut gen: impl FnMut() -> T) -> [T; N] {
		let mut array: [MaybeUninit<T>; N];
		unsafe {
			array = MaybeUninit::uninit().assume_init();
		}
		for elem in &mut array {
			*elem = MaybeUninit::new(gen());
		}
		unsafe { mem::transmute(&array)}
	}
}

But I am getting this error:

error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
  --> src\main.rs:24:12
   |
24 |         unsafe { mem::transmute(&array)}
   |                  ^^^^^^^^^^^^^^
   |
   = note: source type: `&[MaybeUninit<T>; N]` (64 bits)
   = note: target type: `[T; N]` (this type does not have a fixed size)

error: aborting due to previous error

I didn't really understand the reason of error. As far as I know, compiler knows the size of [T; N] while monomorphising the function for a particular T. Is this a bug/limitation? How can I write this code without sacrificing performance?

See:

https://github.com/rust-lang/rust/issues/61956

On nightly you can use the feature-gated MaybeUninit::array_assume_init:

https://github.com/rust-lang/rust/issues/80908

(Or you can use pointer casts, which is what that function does behind the scenes.)

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.