wiwi/chain/
array.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
use crate::prelude_std::*;
use super::{ Chain as _, ChainInner as _ };

super::decl_chain! {
	generics_decl: [T, const N: usize]
	generics: [T, N]
	chain: ArrayChain
	inner: [T; N]
}

impl<T, const N: usize> ArrayChain<T, N> {
	#[inline]
	pub fn new_uninit() -> ArrayChain<MaybeUninit<T>, N> {
		// SAFETY: `MaybeUninit` has no initialisation requirement, so
		// `[MaybeUninit<T>; N]` is always valid
		unsafe { ArrayChain::from_inner(MaybeUninit::uninit().assume_init()) }
	}

	#[inline]
	pub fn new_zeroed() -> ArrayChain<MaybeUninit<T>, N> {
		// SAFETY: `MaybeUninit` has no initialisation requirement, so
		// `[MaybeUninit<T>; N]` is always valid
		unsafe { ArrayChain::from_inner(MaybeUninit::zeroed().assume_init()) }
	}
}

impl<T, const N: usize> ArrayChain<MaybeUninit<T>, N> {
	/// Assumes all slots inside the array are initialised according to `T`'s
	/// requirements, and converts into an array of T
	///
	/// Note: this implementation is currently subpar, as it does fully copy `self`
	/// into a new container using `ptr::read`. We have to do this because, at the
	/// time of writing:
	///
	/// - `transmute` is a bit too dumb, and is not able to prove `[T; N]` and
	///   `[MaybeUninit<T>; N]` are guaranteed to be equal sized, even though
	///   we can see and prove it
	/// - `transmute_unchecked` is like `transmute` but without that compile time
	///   size check, but it is unstable, and according to a code comment will
	///   almost certainly never be stabilised (reasoning is that it's too unsafe,
	///   too much power to give users :p, and to hopefully find other methods for
	///   doing things without it so that it isn't needed)
	/// - `MaybeUninit::array_assume_init` is unstable (it internally makes use of
	///   `transmute_unchecked`)
	///
	/// We don't know of any other option than to perform a ptr cast,
	/// then read from it.
	///
	/// # Safety
	///
	/// All slots in `self` must be fully initialised with valid values of `T`.
	#[inline]
	pub unsafe fn assume_init(self) -> ArrayChain<T, N> {
		#[expect(clippy::as_conversions, reason = "ptr cast")]
		let ptr = self.as_inner() as *const [MaybeUninit<T>; N] as *const [T; N];

		// SAFETY: `ptr` is obtained from `self`, performed valid cast from array
		// of MaybeUninit to array of T, and caller promises that all slots in the
		// array are properly initialised values of `T`. Also see doc comment on
		// this function for why this is a ptr read rather than a transmute
		unsafe { ptr.read().into_chain() }
	}
}