From fb78a9bdc52551753d30acbbe84d52451ea09d56 Mon Sep 17 00:00:00 2001 From: Richard Berry Date: Thu, 2 Mar 2023 09:45:29 +0000 Subject: [PATCH 1/2] Impl conversion from array to Vec --- CHANGELOG.md | 1 + src/sealed.rs | 5 ++++ src/vec.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3248ead1bc..57ce7f5f64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `format` macro. - Added `String::from_utf16`. - Added `is_full`, `recent_index`, `oldest`, and `oldest_index` to `HistoryBuffer` +- Added infallible conversions from arrays to `Vec`. ### Changed diff --git a/src/sealed.rs b/src/sealed.rs index 674be808d0..5e4ef4bba4 100644 --- a/src/sealed.rs +++ b/src/sealed.rs @@ -3,6 +3,11 @@ pub(crate) const fn smaller_than() { Assert::::LESS; } +#[allow(dead_code, path_statements, clippy::no_effect)] +pub(crate) const fn greater_than_eq() { + Assert::::GREATER_EQ; +} + #[allow(dead_code, path_statements, clippy::no_effect)] pub(crate) const fn greater_than_eq_0() { Assert::::GREATER_EQ; diff --git a/src/vec.rs b/src/vec.rs index 6d2a7cf692..f1605f854a 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -1,4 +1,11 @@ -use core::{cmp::Ordering, fmt, hash, iter::FromIterator, mem::MaybeUninit, ops, ptr, slice}; +use core::{ + cmp::Ordering, + fmt, hash, + iter::FromIterator, + mem, + mem::{ManuallyDrop, MaybeUninit}, + ops, ptr, slice, +}; /// A fixed capacity [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html). /// @@ -85,6 +92,45 @@ impl Vec { Ok(v) } + /// Constructs a new vector with a fixed capacity of `N`, initializing + /// it with the provided array. + /// + /// The length of the provided array, `M` may be equal to _or_ less than + /// the capacity of the vector, `N`. + /// + /// 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 { + // Const assert M >= 0 + crate::sealed::greater_than_eq_0::(); + // Const assert N >= M + crate::sealed::greater_than_eq::(); + + // We've got to copy `src`, but we're functionally moving it. Don't run + // any Drop code for T. + let src = ManuallyDrop::new(src); + + if N == M { + Self { + len: 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 = Vec::::new(); + + for (src_elem, dst_elem) in src.iter().zip(v.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) }); + } + + v.len = M; + v + } + } + /// Clones a vec into a new vec pub(crate) fn clone(&self) -> Self where @@ -858,6 +904,12 @@ impl Drop for Vec { } } +impl From<[T; M]> for Vec { + fn from(array: [T; M]) -> Self { + Self::from_array(array) + } +} + impl<'a, T: Clone, const N: usize> TryFrom<&'a [T]> for Vec { type Error = (); @@ -1533,6 +1585,21 @@ mod tests { assert!(Vec::::from_slice(&[1, 2, 3]).is_err()); } + #[test] + fn from_array() { + // Successful construction, N == M + let v: Vec = Vec::from_array([1, 2, 3]); + assert_eq!(v, Vec::::from([1, 2, 3])); + assert_eq!(v.len(), 3); + assert_eq!(v.as_slice(), &[1, 2, 3]); + + // Successful construction, N > M + let v: Vec = Vec::from_array([1, 2, 3]); + assert_eq!(v, Vec::::from([1, 2, 3])); + assert_eq!(v.len(), 3); + assert_eq!(v.as_slice(), &[1, 2, 3]); + } + #[test] fn starts_with() { let v: Vec<_, 8> = Vec::from_slice(b"ab").unwrap(); From 0a5fd1d9fcd4724434859371e9efac8db441e95c Mon Sep 17 00:00:00 2001 From: Richard Berry Date: Thu, 2 Mar 2023 10:29:58 +0000 Subject: [PATCH 2/2] Add test for drop behavior on array to vec conversion --- src/vec.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/vec.rs b/src/vec.rs index f1605f854a..2df7d86c1c 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -1600,6 +1600,23 @@ mod tests { assert_eq!(v.as_slice(), &[1, 2, 3]); } + #[test] + fn from_array_no_drop() { + struct Drops(Option); + + impl Drop for Drops { + fn drop(&mut self) { + self.0 = None; + } + } + + let v: Vec = Vec::from([Drops(Some(1)), Drops(Some(2)), Drops(Some(3))]); + + assert_eq!(v[0].0, Some(1)); + assert_eq!(v[1].0, Some(2)); + assert_eq!(v[2].0, Some(3)); + } + #[test] fn starts_with() { let v: Vec<_, 8> = Vec::from_slice(b"ab").unwrap();