Skip to content
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
.#*
Cargo.lock
target/

# IDE files
.idea
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ of panicking drop implementations.
- Added `from_bytes_truncating_at_nul` to `CString`
- Added `CString::{into_bytes, into_bytes_with_nul, into_string}`
- Added `pop_front_if` and `pop_back_if` to `Deque`
- Made `Vec::from_array` const.

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

Expand Down
80 changes: 79 additions & 1 deletion src/len_type.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
use core::{
fmt::{Debug, Display},
mem,
ops::{Add, AddAssign, Sub, SubAssign},
};

#[allow(non_camel_case_types)]
pub enum TypeEnum {
u8,
u16,
u32,
usize,
}

#[cfg(feature = "zeroize")]
use zeroize::Zeroize;

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

/// The one value of the integer type.
///
Expand Down Expand Up @@ -58,12 +69,13 @@ pub trait Sealed:
}

macro_rules! impl_lentype {
($($(#[$meta:meta])* $LenT:ty),*) => {$(
($($(#[$meta:meta])* $LenT:ident),*) => {$(
$(#[$meta])*
impl Sealed for $LenT {
const ZERO: Self = 0;
const MAX: Self = Self::MAX;
const MAX_USIZE: usize = Self::MAX as _;
const TYPE: TypeEnum = TypeEnum::$LenT;

fn one() -> Self {
1
Expand Down Expand Up @@ -100,3 +112,69 @@ impl_lentype!(
pub const fn check_capacity_fits<LenT: LenType, const N: usize>() {
assert!(LenT::MAX_USIZE >= N, "The capacity is larger than `LenT` can hold, increase the size of `LenT` or reduce the capacity");
}

/// Const cast from [`usize`] to [`LenType`] with `as`.
#[inline]
pub const fn as_len_type<L: LenType>(n: usize) -> L {
// SAFETY: transmute is safe since after cast we cast to the same type.
unsafe {
// ALWAYS compiletime switch.
match L::TYPE {
// transmute_copy, instead of transmute - because `L`
// is a "dependent type".
TypeEnum::u8 => mem::transmute_copy(&(n as u8)),
TypeEnum::u16 => mem::transmute_copy(&(n as u16)),
TypeEnum::u32 => mem::transmute_copy(&(n as u32)),
TypeEnum::usize => mem::transmute_copy(&n),
}
}
}

/// Checked cast to [`LenType`].
///
/// # Panic
///
/// Panics if `n` is outside of `L` range.
#[inline]
pub const fn to_len_type<L: LenType>(n: usize) -> L {
try_to_len_type(n).unwrap()
}

/// Checked cast to [`LenType`].
///
/// Returns `None` if `n` is outside of `L` range.
#[inline]
pub const fn try_to_len_type<L: LenType>(n: usize) -> Option<L> {
if n > L::MAX_USIZE {
return None;
}
Some(as_len_type(n))
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_len_cast() {
// 1. Check constness
const {
assert!(to_len_type::<u8>(150) == 150);
assert!(to_len_type::<u16>(15_000) == 15_000);
assert!(to_len_type::<u32>(1_500_000) == 1_500_000);
assert!(to_len_type::<usize>(usize::MAX) == usize::MAX);
}
// 2. Check correctness
fn check<T: LenType>() {
const COUNT: usize = 100;
for i in 0..COUNT {
let n = i * (T::MAX_USIZE / COUNT);
assert_eq!(to_len_type::<T>(n).into_usize(), n);
}
}
check::<u8>();
check::<u16>();
check::<u32>();
check::<usize>();
}
}
42 changes: 21 additions & 21 deletions src/vec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use core::{
fmt, hash,
iter::FusedIterator,
marker::PhantomData,
mem::{self, ManuallyDrop, MaybeUninit},
mem::{ManuallyDrop, MaybeUninit},
ops::{self, Range, RangeBounds},
ptr::{self, NonNull},
slice,
Expand All @@ -16,7 +16,7 @@ use core::{
use zeroize::Zeroize;

use crate::{
len_type::{check_capacity_fits, LenType},
len_type::{check_capacity_fits, to_len_type, LenType},
CapacityError,
};

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

if N == M {
Self {
phantom: PhantomData,
len: LenT::from_usize(N),
// NOTE(unsafe) ManuallyDrop<[T; M]> and [MaybeUninit<T>; N]
// have the same layout when N == M.
buffer: unsafe { mem::transmute_copy(&src) },
}
} else {
let mut v = Self::new();
let len: LenT = to_len_type(M);

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

unsafe { v.set_len(M) };
v
}
// MaybeUninit::deref is non-const, so we just cast to it's internal value.
let src_ptr: *const T = ptr::from_ref(&src).cast();

// Cast from buffer's [MaybeUninit<T>] to [T].
let dst_ptr: *mut T = v.buffer.buffer.as_mut_ptr().cast();

// SAFETY: Move/copy data from src to v's internal buffer.
// * Using src_ptr as `*const T` is safe since src's ManuallyDrop<[T; M]> is transparent.
// * Using dst_ptr as `*mut T` is safe since v's [MaybeUninit<T>; N] is an array of
// transparent types.
unsafe { ptr::copy_nonoverlapping(src_ptr, dst_ptr, M) };
v.len = len;

v
}

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