Skip to content

Commit 666a0ac

Browse files
tower120sgued
authored andcommitted
Vec::from_array now const
1 parent 0db1fb3 commit 666a0ac

File tree

3 files changed

+101
-22
lines changed

3 files changed

+101
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ of panicking drop implementations.
1212
- Added `from_bytes_truncating_at_nul` to `CString`
1313
- Added `CString::{into_bytes, into_bytes_with_nul, into_string}`
1414
- Added `pop_front_if` and `pop_back_if` to `Deque`
15+
- Made `Vec::from_array` const.
1516

1617
## [v0.9.2] 2025-11-12
1718

src/len_type.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
use core::{
22
fmt::{Debug, Display},
3+
mem,
34
ops::{Add, AddAssign, Sub, SubAssign},
45
};
56

7+
#[allow(non_camel_case_types)]
8+
pub enum TypeEnum {
9+
u8,
10+
u16,
11+
u32,
12+
usize,
13+
}
14+
615
#[cfg(feature = "zeroize")]
716
use zeroize::Zeroize;
817

@@ -27,6 +36,8 @@ pub trait Sealed:
2736
const MAX: Self;
2837
/// The maximum value of this type, as a `usize`.
2938
const MAX_USIZE: usize;
39+
/// This type as an enum.
40+
const TYPE: TypeEnum;
3041

3142
/// The one value of the integer type.
3243
///
@@ -58,12 +69,13 @@ pub trait Sealed:
5869
}
5970

6071
macro_rules! impl_lentype {
61-
($($(#[$meta:meta])* $LenT:ty),*) => {$(
72+
($($(#[$meta:meta])* $LenT:ident),*) => {$(
6273
$(#[$meta])*
6374
impl Sealed for $LenT {
6475
const ZERO: Self = 0;
6576
const MAX: Self = Self::MAX;
6677
const MAX_USIZE: usize = Self::MAX as _;
78+
const TYPE: TypeEnum = TypeEnum::$LenT;
6779

6880
fn one() -> Self {
6981
1
@@ -100,3 +112,69 @@ impl_lentype!(
100112
pub const fn check_capacity_fits<LenT: LenType, const N: usize>() {
101113
assert!(LenT::MAX_USIZE >= N, "The capacity is larger than `LenT` can hold, increase the size of `LenT` or reduce the capacity");
102114
}
115+
116+
/// Const cast from [`usize`] to [`LenType`] with `as`.
117+
#[inline]
118+
pub const fn as_len_type<L: LenType>(n: usize) -> L {
119+
// SAFETY: transmute is safe since after cast we cast to the same type.
120+
unsafe {
121+
// ALWAYS compiletime switch.
122+
match L::TYPE {
123+
// transmute_copy, instead of transmute - because `L`
124+
// is a "dependent type".
125+
TypeEnum::u8 => mem::transmute_copy(&(n as u8)),
126+
TypeEnum::u16 => mem::transmute_copy(&(n as u16)),
127+
TypeEnum::u32 => mem::transmute_copy(&(n as u32)),
128+
TypeEnum::usize => mem::transmute_copy(&n),
129+
}
130+
}
131+
}
132+
133+
/// Checked cast to [`LenType`].
134+
///
135+
/// # Panic
136+
///
137+
/// Panics if `n` is outside of `L` range.
138+
#[inline]
139+
pub const fn to_len_type<L: LenType>(n: usize) -> L {
140+
try_to_len_type(n).unwrap()
141+
}
142+
143+
/// Checked cast to [`LenType`].
144+
///
145+
/// Returns `None` if `n` is outside of `L` range.
146+
#[inline]
147+
pub const fn try_to_len_type<L: LenType>(n: usize) -> Option<L> {
148+
if n > L::MAX_USIZE {
149+
return None;
150+
}
151+
Some(as_len_type(n))
152+
}
153+
154+
#[cfg(test)]
155+
mod tests {
156+
use super::*;
157+
158+
#[test]
159+
fn test_len_cast() {
160+
// 1. Check constness
161+
const {
162+
assert!(to_len_type::<u8>(150) == 150);
163+
assert!(to_len_type::<u16>(15_000) == 15_000);
164+
assert!(to_len_type::<u32>(1_500_000) == 1_500_000);
165+
assert!(to_len_type::<usize>(usize::MAX) == usize::MAX);
166+
}
167+
// 2. Check correctness
168+
fn check<T: LenType>() {
169+
const COUNT: usize = 100;
170+
for i in 0..COUNT {
171+
let n = i * (T::MAX_USIZE / COUNT);
172+
assert_eq!(to_len_type::<T>(n).into_usize(), n);
173+
}
174+
}
175+
check::<u8>();
176+
check::<u16>();
177+
check::<u32>();
178+
check::<usize>();
179+
}
180+
}

src/vec/mod.rs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use core::{
66
fmt, hash,
77
iter::FusedIterator,
88
marker::PhantomData,
9-
mem::{self, ManuallyDrop, MaybeUninit},
9+
mem::{ManuallyDrop, MaybeUninit},
1010
ops::{self, Range, RangeBounds},
1111
ptr::{self, NonNull},
1212
slice,
@@ -16,7 +16,7 @@ use core::{
1616
use zeroize::Zeroize;
1717

1818
use crate::{
19-
len_type::{check_capacity_fits, LenType},
19+
len_type::{check_capacity_fits, to_len_type, LenType},
2020
CapacityError,
2121
};
2222

@@ -355,7 +355,7 @@ impl<T, LenT: LenType, const N: usize> Vec<T, N, LenT> {
355355
///
356356
/// If the length of the provided array is greater than the capacity of the
357357
/// vector a compile-time error will be produced.
358-
pub fn from_array<const M: usize>(src: [T; M]) -> Self {
358+
pub const fn from_array<const M: usize>(src: [T; M]) -> Self {
359359
const {
360360
assert!(N >= M);
361361
}
@@ -364,26 +364,24 @@ impl<T, LenT: LenType, const N: usize> Vec<T, N, LenT> {
364364
// any Drop code for T.
365365
let src = ManuallyDrop::new(src);
366366

367-
if N == M {
368-
Self {
369-
phantom: PhantomData,
370-
len: LenT::from_usize(N),
371-
// NOTE(unsafe) ManuallyDrop<[T; M]> and [MaybeUninit<T>; N]
372-
// have the same layout when N == M.
373-
buffer: unsafe { mem::transmute_copy(&src) },
374-
}
375-
} else {
376-
let mut v = Self::new();
367+
let len: LenT = to_len_type(M);
377368

378-
for (src_elem, dst_elem) in src.iter().zip(v.buffer.buffer.iter_mut()) {
379-
// NOTE(unsafe) src element is not going to drop as src itself
380-
// is wrapped in a ManuallyDrop.
381-
dst_elem.write(unsafe { ptr::read(src_elem) });
382-
}
369+
let mut v = Self::new();
383370

384-
unsafe { v.set_len(M) };
385-
v
386-
}
371+
// MaybeUninit::deref is non-const, so we just cast to it's internal value.
372+
let src_ptr: *const T = ptr::from_ref(&src).cast();
373+
374+
// Cast from buffer's [MaybeUninit<T>] to [T].
375+
let dst_ptr: *mut T = v.buffer.buffer.as_mut_ptr().cast();
376+
377+
// SAFETY: Move/copy data from src to v's internal buffer.
378+
// * Using src_ptr as `*const T` is safe since src's ManuallyDrop<[T; M]> is transparent.
379+
// * Using dst_ptr as `*mut T` is safe since v's [MaybeUninit<T>; N] is an array of
380+
// transparent types.
381+
unsafe { ptr::copy_nonoverlapping(src_ptr, dst_ptr, M) };
382+
v.len = len;
383+
384+
v
387385
}
388386

389387
/// Returns the contents of the vector as an array of length `M` if the length
@@ -466,6 +464,8 @@ impl<T, LenT: LenType, S: VecStorage<T> + ?Sized> VecInner<T, LenT, S> {
466464
/// [`mem::forget`], for example), the vector may have lost and leaked
467465
/// elements arbitrarily, including elements outside the range.
468466
///
467+
/// [`mem::forget`]: core::mem::forget
468+
///
469469
/// # Examples
470470
///
471471
/// ```

0 commit comments

Comments
 (0)