diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c561d8d..0ddbe04c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Implement `Default` for `CapacityError`. - Implement `defmt::Format` for `CapacityError`. +- Implement `TryFrom` for `Deque` from array. ## [v0.9.1] - 2025-08-19 diff --git a/src/deque.rs b/src/deque.rs index 446cebbd..f318859e 100644 --- a/src/deque.rs +++ b/src/deque.rs @@ -33,15 +33,15 @@ //! } //! ``` +use crate::vec::{OwnedVecStorage, VecStorage, VecStorageInner, ViewVecStorage}; +use crate::CapacityError; use core::cmp::Ordering; use core::fmt; use core::iter::FusedIterator; use core::marker::PhantomData; -use core::mem::MaybeUninit; +use core::mem::{ManuallyDrop, MaybeUninit}; use core::{ptr, slice}; -use crate::vec::{OwnedVecStorage, VecStorage, VecStorageInner, ViewVecStorage}; - /// Base struct for [`Deque`] and [`DequeView`], generic over the [`VecStorage`]. /// /// In most cases you should use [`Deque`] or [`DequeView`] directly. Only use this @@ -999,11 +999,55 @@ impl PartialEq for Deque { impl Eq for Deque {} +impl TryFrom<[T; NS]> for Deque { + /// Converts a `[T; NS]` into a `Deque`. + /// + /// ``` + /// use heapless::Deque; + /// + /// let deq1 = Deque::::try_from([1, 2, 3]).unwrap(); + /// let mut deq2 = Deque::::new(); + /// deq2.push_back(1).unwrap(); + /// deq2.push_back(2).unwrap(); + /// deq2.push_back(3).unwrap(); + /// + /// assert_eq!(deq1, deq2); + /// ``` + type Error = (CapacityError, [T; NS]); + + /// Converts a `[T; NS]` array into a `Deque`. + /// + /// Returns back the `value` if NS > ND. + fn try_from(value: [T; NS]) -> Result { + if NS > ND { + return Err((CapacityError, value)); + } + + let mut deq = Self::default(); + let value = ManuallyDrop::new(value); + + // SAFETY: We already ensured that value fits in deq. + unsafe { + ptr::copy_nonoverlapping( + value.as_ptr(), + deq.buffer.buffer.as_mut_ptr().cast::(), + NS, + ); + } + + deq.front = 0; + deq.back = NS; + deq.full = NS == ND; + + Ok(deq) + } +} + #[cfg(test)] mod tests { - use static_assertions::assert_not_impl_any; - use super::Deque; + use crate::CapacityError; + use static_assertions::assert_not_impl_any; // Ensure a `Deque` containing `!Send` values stays `!Send` itself. assert_not_impl_any!(Deque<*const (), 4>: Send); @@ -1545,4 +1589,89 @@ mod tests { assert_eq!(a, b); } + + #[test] + fn try_from_array() { + // Array is too big error. + assert!(matches!( + Deque::::try_from([1, 2, 3, 4]), + Err((CapacityError, [1, 2, 3, 4])) + )); + + // Array is at limit. + let deq1 = Deque::::try_from([1, 2, 3]).unwrap(); + let mut deq2 = Deque::::new(); + deq2.push_back(1).unwrap(); + deq2.push_back(2).unwrap(); + deq2.push_back(3).unwrap(); + assert!(deq1.is_full()); + assert_eq!(deq1, deq2); + + // Array is under limit. + let deq1 = Deque::::try_from([1, 2, 3, 4]).unwrap(); + let mut deq2 = Deque::::new(); + deq2.push_back(1).unwrap(); + deq2.push_back(2).unwrap(); + deq2.push_back(3).unwrap(); + deq2.push_back(4).unwrap(); + + assert!(!deq1.is_full()); + assert_eq!(deq1, deq2); + } + + #[test] + fn try_from_array_with_zst() { + #[derive(Debug, PartialEq, Copy, Clone)] + struct ZeroSizedType; + + // Test with ZST (zero-sized type) + let deq1 = + Deque::::try_from([ZeroSizedType, ZeroSizedType, ZeroSizedType]) + .unwrap(); + let mut deq2 = Deque::::new(); + deq2.push_back(ZeroSizedType).unwrap(); + deq2.push_back(ZeroSizedType).unwrap(); + deq2.push_back(ZeroSizedType).unwrap(); + + assert_eq!(deq1, deq2); + assert_eq!(deq1.len(), 3); + } + + #[test] + fn try_from_array_drop() { + droppable!(); + + // Array is over limit. + { + let _result = Deque::::try_from([ + Droppable::new(), + Droppable::new(), + Droppable::new(), + ]); + } + + assert_eq!(Droppable::count(), 0); + + // Array is at limit. + { + let _result = Deque::::try_from([ + Droppable::new(), + Droppable::new(), + Droppable::new(), + ]); + } + + assert_eq!(Droppable::count(), 0); + + // Array is under limit. + { + let _result = Deque::::try_from([ + Droppable::new(), + Droppable::new(), + Droppable::new(), + ]); + } + + assert_eq!(Droppable::count(), 0); + } }