diff --git a/.gitignore b/.gitignore index e64a511ae8..4f6992fc3e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ .#* Cargo.lock target/ + +# IDE files +.idea \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a282124f4..4d679f5b78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - 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 diff --git a/src/len_type.rs b/src/len_type.rs index 0eb6ff6a56..7dd45c9102 100644 --- a/src/len_type.rs +++ b/src/len_type.rs @@ -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; @@ -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. /// @@ -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 @@ -100,3 +112,69 @@ impl_lentype!( pub const fn check_capacity_fits() { 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(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(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(n: usize) -> Option { + 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::(150) == 150); + assert!(to_len_type::(15_000) == 15_000); + assert!(to_len_type::(1_500_000) == 1_500_000); + assert!(to_len_type::(usize::MAX) == usize::MAX); + } + // 2. Check correctness + fn check() { + const COUNT: usize = 100; + for i in 0..COUNT { + let n = i * (T::MAX_USIZE / COUNT); + assert_eq!(to_len_type::(n).into_usize(), n); + } + } + check::(); + check::(); + check::(); + check::(); + } +} diff --git a/src/vec/mod.rs b/src/vec/mod.rs index 093bdb13d2..37d0ba820c 100644 --- a/src/vec/mod.rs +++ b/src/vec/mod.rs @@ -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, @@ -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, }; @@ -355,7 +355,7 @@ impl Vec { /// /// 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(src: [T; M]) -> Self { + pub const fn from_array(src: [T; M]) -> Self { const { assert!(N >= M); } @@ -364,26 +364,24 @@ impl Vec { // 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; 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] 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; 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 @@ -466,6 +464,8 @@ impl + ?Sized> VecInner { /// [`mem::forget`], for example), the vector may have lost and leaked /// elements arbitrarily, including elements outside the range. /// + /// [`mem::forget`]: core::mem::forget + /// /// # Examples /// /// ```