From 0b027b1ff8bb0ffeea0fa34791a15c41e2acd8fc Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Khan Date: Tue, 16 Sep 2025 14:19:02 +0200 Subject: [PATCH] Implements zeroization support across all heapless data structures to securely clear sensitive data from memory: - When the zeroize feature is enabled, the LenType sealed trait now has Zeroize as a supertrait - This simplifies the bound for deriving Zeroize for VecInner and other types - Added tests to verify VecView also implements Zeroize correctly This feature is essential for security-sensitive applications needing to prevent data leaks from memory dumps. Note: Zeroize initially worked on Vector purely via derivation, however was not complete without proper bound checks. Without these checks, the deref implementation of Zeroize was used instead, which led to incomplete zeroization of the Vector's contents. --- .github/workflows/build.yml | 5 +-- CHANGELOG.md | 1 + Cargo.toml | 4 +++ src/binary_heap.rs | 26 ++++++++++++++ src/c_string.rs | 46 ++++++++++++++++++++++++ src/deque.rs | 29 +++++++++++++++ src/history_buf.rs | 43 ++++++++++++++++++++-- src/index_map.rs | 41 +++++++++++++++++++++ src/index_set.rs | 28 +++++++++++++++ src/len_type.rs | 12 +++++++ src/lib.rs | 10 ++++++ src/linear_map.rs | 28 +++++++++++++++ src/sorted_linked_list.rs | 56 +++++++++++++++++++++++++++++ src/string/mod.rs | 26 ++++++++++++++ src/vec/mod.rs | 71 +++++++++++++++++++++++++++++++++++++ 15 files changed, 422 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7eede1ce..dbb73106 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,7 +48,7 @@ jobs: components: miri - name: Run miri - run: MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --features="alloc,defmt,mpmc_large,portable-atomic-critical-section,serde,ufmt,bytes" + run: MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --features="alloc,defmt,mpmc_large,portable-atomic-critical-section,serde,ufmt,bytes,zeroize" # Run cargo test test: @@ -84,7 +84,7 @@ jobs: toolchain: stable - name: Run cargo test - run: cargo test --features="alloc,defmt,mpmc_large,portable-atomic-critical-section,serde,ufmt,bytes" + run: cargo test --features="alloc,defmt,mpmc_large,portable-atomic-critical-section,serde,ufmt,bytes,zeroize" # Run cargo fmt --check style: @@ -176,6 +176,7 @@ jobs: cargo check --target="${target}" --features="portable-atomic-critical-section" cargo check --target="${target}" --features="serde" cargo check --target="${target}" --features="ufmt" + cargo check --target="${target}" --features="zeroize" env: target: ${{ matrix.target }} diff --git a/CHANGELOG.md b/CHANGELOG.md index ddb1d69b..14bb3511 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Implement `defmt::Format` for `CapacityError`. - Implement `TryFrom` for `Deque` from array. - Switch from `serde` to `serde_core` for enabling faster compilations. +- Implement `Zeroize` trait for all data structures with the `zeroize` feature to securely clear sensitive data from memory. ## [v0.9.1] - 2025-08-19 diff --git a/Cargo.toml b/Cargo.toml index 76e2e0be..7390c86b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,9 @@ ufmt = ["dep:ufmt", "dep:ufmt-write"] # Implement `defmt::Format`. defmt = ["dep:defmt"] +# Implement `zeroize::Zeroize` trait. +zeroize = ["dep:zeroize"] + # Enable larger MPMC sizes. mpmc_large = [] @@ -63,6 +66,7 @@ serde_core = { version = "1", optional = true, default-features = false } ufmt = { version = "0.2", optional = true } ufmt-write = { version = "0.1", optional = true } defmt = { version = "1.0.1", optional = true } +zeroize = { version = "1.8", optional = true, default-features = false, features = ["derive"] } # for the pool module [target.'cfg(any(target_arch = "arm", target_pointer_width = "32", target_pointer_width = "64"))'.dependencies] diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 22b882a1..452b84a1 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -17,6 +17,9 @@ use core::{ ptr, slice, }; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + use crate::vec::{OwnedVecStorage, Vec, VecInner, VecStorage, ViewVecStorage}; /// Min-heap @@ -55,6 +58,11 @@ impl private::Sealed for Min {} /// /// In most cases you should use [`BinaryHeap`] or [`BinaryHeapView`] directly. Only use this /// struct if you want to write code that's generic over both. +#[cfg_attr( + feature = "zeroize", + derive(Zeroize), + zeroize(bound = "T: Zeroize, S: Zeroize") +)] pub struct BinaryHeapInner + ?Sized> { pub(crate) _kind: PhantomData, pub(crate) data: VecInner, @@ -882,6 +890,24 @@ mod tests { assert_eq!(heap.pop(), None); } + #[test] + #[cfg(feature = "zeroize")] + fn test_binary_heap_zeroize() { + use zeroize::Zeroize; + + let mut heap = BinaryHeap::::new(); + for i in 0..8 { + heap.push(i).unwrap(); + } + + assert_eq!(heap.len(), 8); + assert_eq!(heap.peek(), Some(&7)); + + // zeroized using Vec's implementation + heap.zeroize(); + assert_eq!(heap.len(), 0); + } + fn _test_variance<'a: 'b, 'b>(x: BinaryHeap<&'a (), Max, 42>) -> BinaryHeap<&'b (), Max, 42> { x } diff --git a/src/c_string.rs b/src/c_string.rs index 25df69b7..cf4ce301 100644 --- a/src/c_string.rs +++ b/src/c_string.rs @@ -10,6 +10,9 @@ use core::{ ops::Deref, }; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + /// A fixed capacity [`CString`](https://doc.rust-lang.org/std/ffi/struct.CString.html). /// /// It stores up to `N - 1` non-nul characters with a trailing nul terminator. @@ -18,6 +21,20 @@ pub struct CString { inner: Vec, } +#[cfg(feature = "zeroize")] +impl Zeroize for CString { + fn zeroize(&mut self) { + self.inner.zeroize(); + + const { + assert!(N > 0); + } + + // SAFETY: We just asserted that `N > 0`. + unsafe { self.inner.push_unchecked(b'\0') }; + } +} + impl CString { /// Creates a new C-compatible string with a terminating nul byte. /// @@ -483,6 +500,35 @@ mod tests { assert_eq!(Borrow::::borrow(&string), c"foo"); } + #[test] + #[cfg(feature = "zeroize")] + fn test_cstring_zeroize() { + use zeroize::Zeroize; + + let mut c_string = CString::<32>::from_bytes_with_nul(b"sensitive_password\0").unwrap(); + + assert_eq!(c_string.to_str(), Ok("sensitive_password")); + assert!(!c_string.to_bytes().is_empty()); + let original_length = c_string.to_bytes().len(); + assert_eq!(original_length, 18); + + let new_string = CString::<32>::from_bytes_with_nul(b"short\0").unwrap(); + c_string = new_string; + + assert_eq!(c_string.to_str(), Ok("short")); + assert_eq!(c_string.to_bytes().len(), 5); + + // zeroized using Vec's implementation + c_string.zeroize(); + + assert_eq!(c_string.to_bytes().len(), 0); + assert_eq!(c_string.to_bytes_with_nul(), &[0]); + + c_string.extend_from_bytes(b"new_data").unwrap(); + assert_eq!(c_string.to_str(), Ok("new_data")); + assert_eq!(c_string.to_bytes().len(), 8); + } + mod equality { use super::*; diff --git a/src/deque.rs b/src/deque.rs index f318859e..ec12bbf8 100644 --- a/src/deque.rs +++ b/src/deque.rs @@ -42,10 +42,14 @@ use core::marker::PhantomData; use core::mem::{ManuallyDrop, MaybeUninit}; use core::{ptr, slice}; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + /// Base struct for [`Deque`] and [`DequeView`], generic over the [`VecStorage`]. /// /// In most cases you should use [`Deque`] or [`DequeView`] directly. Only use this /// struct if you want to write code that's generic over both. +#[cfg_attr(feature = "zeroize", derive(Zeroize))] pub struct DequeInner + ?Sized> { // This phantomdata is required because otherwise rustc thinks that `T` is not used phantom: PhantomData, @@ -1674,4 +1678,29 @@ mod tests { assert_eq!(Droppable::count(), 0); } + + #[test] + #[cfg(feature = "zeroize")] + fn test_deque_zeroize() { + use zeroize::Zeroize; + + let mut deque = Deque::::new(); + + for i in 1..=8 { + deque.push_back(i).unwrap(); + } + for i in 9..=16 { + deque.push_front(i).unwrap(); + } + + assert_eq!(deque.len(), 16); + assert_eq!(deque.front(), Some(&16)); + assert_eq!(deque.back(), Some(&8)); + + // zeroized using Vec's implementation + deque.zeroize(); + + assert_eq!(deque.len(), 0); + assert!(deque.is_empty()); + } } diff --git a/src/history_buf.rs b/src/history_buf.rs index b06e4648..67df4cd7 100644 --- a/src/history_buf.rs +++ b/src/history_buf.rs @@ -38,10 +38,14 @@ use core::ops::Deref; use core::ptr; use core::slice; -mod storage { - use core::mem::MaybeUninit; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; +mod storage { use super::{HistoryBufInner, HistoryBufView}; + use core::mem::MaybeUninit; + #[cfg(feature = "zeroize")] + use zeroize::Zeroize; /// Trait defining how data for a container is stored. /// @@ -83,6 +87,7 @@ mod storage { } // One sealed layer of indirection to hide the internal details (The MaybeUninit). + #[cfg_attr(feature = "zeroize", derive(Zeroize))] pub struct HistoryBufStorageInner { pub(crate) buffer: T, } @@ -148,6 +153,7 @@ use self::storage::HistoryBufSealedStorage; /// /// In most cases you should use [`HistoryBuf`] or [`HistoryBufView`] directly. Only use this /// struct if you want to write code that's generic over both. +#[cfg_attr(feature = "zeroize", derive(Zeroize))] pub struct HistoryBufInner + ?Sized> { // This phantomdata is required because otherwise rustc thinks that `T` is not used phantom: PhantomData, @@ -938,6 +944,39 @@ mod tests { assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3); } + #[test] + #[cfg(feature = "zeroize")] + fn test_history_buf_zeroize() { + use zeroize::Zeroize; + + let mut buffer = HistoryBuf::::new(); + for i in 0..8 { + buffer.write(i); + } + + assert_eq!(buffer.len(), 8); + assert_eq!(buffer.recent(), Some(&7)); + + // Clear to mark formerly used memory as unused, to make sure that it also gets zeroed + buffer.clear(); + + buffer.write(20); + assert_eq!(buffer.len(), 1); + assert_eq!(buffer.recent(), Some(&20)); + + buffer.zeroize(); + + assert_eq!(buffer.len(), 0); + assert!(buffer.is_empty()); + + // Check that all underlying memory actually got zeroized + unsafe { + for a in buffer.data.buffer { + assert_eq!(a.assume_init(), 0); + } + } + } + fn _test_variance<'a: 'b, 'b>(x: HistoryBuf<&'a (), 42>) -> HistoryBuf<&'b (), 42> { x } diff --git a/src/index_map.rs b/src/index_map.rs index 34846240..e990c1e0 100644 --- a/src/index_map.rs +++ b/src/index_map.rs @@ -8,6 +8,9 @@ use core::{ ops, slice, }; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + use hash32::{BuildHasherDefault, FnvHasher}; use crate::Vec; @@ -66,6 +69,7 @@ use crate::Vec; pub type FnvIndexMap = IndexMap, N>; #[derive(Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "zeroize", derive(Zeroize))] struct HashValue(u16); impl HashValue { @@ -80,6 +84,7 @@ impl HashValue { #[doc(hidden)] #[derive(Clone)] +#[cfg_attr(feature = "zeroize", derive(Zeroize))] pub struct Bucket { hash: HashValue, key: K, @@ -88,6 +93,7 @@ pub struct Bucket { #[doc(hidden)] #[derive(Clone, Copy, PartialEq)] +#[cfg_attr(feature = "zeroize", derive(Zeroize))] pub struct Pos { // compact representation of `{ hash_value: u16, index: u16 }` // To get the most from `NonZero` we store the *value minus 1*. This way `None::Option` @@ -138,6 +144,11 @@ macro_rules! probe_loop { } } +#[cfg_attr( + feature = "zeroize", + derive(Zeroize), + zeroize(bound = "K: Zeroize, V: Zeroize") +)] struct CoreMap { entries: Vec, N, usize>, indices: [Option; N], @@ -722,8 +733,14 @@ where /// println!("{}: \"{}\"", book, review); /// } /// ``` +#[cfg_attr( + feature = "zeroize", + derive(Zeroize), + zeroize(bound = "K: Zeroize, V: Zeroize") +)] pub struct IndexMap { core: CoreMap, + #[cfg_attr(feature = "zeroize", zeroize(skip))] build_hasher: S, } @@ -1988,4 +2005,28 @@ mod tests { let map: FnvIndexMap = Default::default(); assert_eq!(map, map); } + + #[test] + #[cfg(feature = "zeroize")] + fn test_index_map_zeroize() { + use zeroize::Zeroize; + + let mut map: FnvIndexMap = FnvIndexMap::new(); + for i in 1..=8 { + map.insert(i, i * 10).unwrap(); + } + + assert_eq!(map.len(), 8); + assert!(!map.is_empty()); + + // zeroized using Vec's implementation + map.zeroize(); + + assert_eq!(map.len(), 0); + assert!(map.is_empty()); + + map.insert(1, 10).unwrap(); + assert_eq!(map.len(), 1); + assert_eq!(map.get(&1), Some(&10)); + } } diff --git a/src/index_set.rs b/src/index_set.rs index 53fd0ea3..803fa291 100644 --- a/src/index_set.rs +++ b/src/index_set.rs @@ -5,6 +5,9 @@ use core::{ hash::{BuildHasher, Hash}, }; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + use hash32::{BuildHasherDefault, FnvHasher}; use crate::index_map::{self, IndexMap}; @@ -85,6 +88,7 @@ pub type FnvIndexSet = IndexSet { map: IndexMap, } @@ -696,4 +700,28 @@ mod tests { // Ensure a `IndexSet` containing `!Send` values stays `!Send` itself. assert_not_impl_any!(IndexSet<*const (), BuildHasherDefault<()>, 4>: Send); + + #[test] + #[cfg(feature = "zeroize")] + fn test_index_set_zeroize() { + use zeroize::Zeroize; + + let mut set: IndexSet, 8> = IndexSet::new(); + for i in 1..=8 { + set.insert(i).unwrap(); + } + + assert_eq!(set.len(), 8); + assert!(set.contains(&8)); + + // zeroized using index_map's implementation + set.zeroize(); + + assert_eq!(set.len(), 0); + assert!(set.is_empty()); + + set.insert(1).unwrap(); + assert_eq!(set.len(), 1); + assert!(set.contains(&1)); + } } diff --git a/src/len_type.rs b/src/len_type.rs index d42a659d..0eb6ff6a 100644 --- a/src/len_type.rs +++ b/src/len_type.rs @@ -3,6 +3,9 @@ use core::{ ops::{Add, AddAssign, Sub, SubAssign}, }; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + pub trait Sealed: Send + Sync @@ -75,6 +78,15 @@ macro_rules! impl_lentype { /// A sealed trait representing a valid type to use as a length for a container. /// /// This cannot be implemented in user code, and is restricted to `u8`, `u16`, `u32`, and `usize`. +/// +/// When the `zeroize` feature is enabled, this trait requires the `Zeroize` trait. +#[cfg(feature = "zeroize")] +pub trait LenType: Sealed + Zeroize {} + +/// A sealed trait representing a valid type to use as a length for a container. +/// +/// This cannot be implemented in user code, and is restricted to `u8`, `u16`, `u32`, and `usize`. +#[cfg(not(feature = "zeroize"))] pub trait LenType: Sealed {} impl_lentype!( diff --git a/src/lib.rs b/src/lib.rs index 656999ee..fd506a92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,6 +106,16 @@ //! - [`mpmc::MpMcQueue`](mpmc): A lock-free multiple-producer, multiple-consumer queue. //! - [`spsc::Queue`](spsc): A lock-free single-producer, single-consumer queue. //! +//! # Zeroize Support +//! +//! The `zeroize` feature enables secure memory wiping for the data structures via the [`zeroize`](https://crates.io/crates/zeroize) +//! crate. Sensitive data can be properly erased from memory when no longer needed. +//! +//! When zeroizing a container, all underlying memory (including unused portion of the containers) +//! is overwritten with zeros, length counters are reset, and the container is left in a valid but +//! empty state that can be reused. +//! +//! Check the [documentation of the zeroize crate](https://docs.rs/zeroize/) for more information. //! # Minimum Supported Rust Version (MSRV) //! //! This crate does *not* have a Minimum Supported Rust Version (MSRV) and may make use of language diff --git a/src/linear_map.rs b/src/linear_map.rs index b7051737..c7f34278 100644 --- a/src/linear_map.rs +++ b/src/linear_map.rs @@ -4,6 +4,9 @@ use core::{borrow::Borrow, fmt, mem, ops, slice}; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + use crate::vec::{OwnedVecStorage, Vec, VecInner, ViewVecStorage}; mod storage { @@ -88,6 +91,11 @@ pub type OwnedStorage = OwnedVecStorage<(K, V), N>; pub type ViewStorage = ViewVecStorage<(K, V)>; /// Base struct for [`LinearMap`] and [`LinearMapView`] +#[cfg_attr( + feature = "zeroize", + derive(Zeroize), + zeroize(bound = "S: Zeroize, K: Zeroize, V: Zeroize") +)] pub struct LinearMapInner + ?Sized> { pub(crate) buffer: VecInner<(K, V), usize, S>, } @@ -752,4 +760,24 @@ mod test { let map: LinearMap = Default::default(); assert_eq!(map, map); } + + #[test] + #[cfg(feature = "zeroize")] + fn test_linear_map_zeroize() { + use zeroize::Zeroize; + + let mut map: LinearMap = LinearMap::new(); + for i in 1..=8 { + map.insert(i, i * 10).unwrap(); + } + + assert_eq!(map.len(), 8); + assert_eq!(map.get(&5), Some(&50)); + + // zeroized using Vec's implementation + map.zeroize(); + + assert_eq!(map.len(), 0); + assert!(map.is_empty()); + } } diff --git a/src/sorted_linked_list.rs b/src/sorted_linked_list.rs index d319fe0f..0b40b454 100644 --- a/src/sorted_linked_list.rs +++ b/src/sorted_linked_list.rs @@ -33,6 +33,9 @@ use core::mem::MaybeUninit; use core::ops::{Deref, DerefMut}; use core::ptr; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + mod storage { use super::{LenType, Node, SortedLinkedListInner, SortedLinkedListView}; @@ -191,6 +194,7 @@ impl private::Sealed for Max {} impl private::Sealed for Min {} /// A node in the [`SortedLinkedList`]. +#[cfg_attr(feature = "zeroize", derive(Zeroize))] pub struct Node { val: MaybeUninit, next: Idx, @@ -845,6 +849,26 @@ where } } +#[cfg(feature = "zeroize")] +impl Zeroize for SortedLinkedListInner +where + T: Ord + Zeroize, + Idx: LenType + Zeroize, + K: Kind, + S: SortedLinkedListStorage + ?Sized, +{ + fn zeroize(&mut self) { + while let Some(mut item) = self.pop() { + item.zeroize(); + } + + let buffer = self.list.borrow_mut(); + for elem in buffer { + elem.zeroize(); + } + } +} + #[cfg(test)] mod tests { use static_assertions::assert_not_impl_any; @@ -974,6 +998,38 @@ mod tests { assert_eq!(ll.peek().unwrap(), &1001); } + #[test] + #[cfg(feature = "zeroize")] + fn test_sorted_linked_list_zeroize() { + use zeroize::Zeroize; + + let mut list: SortedLinkedList = SortedLinkedList::new_u8(); + for i in 1..=8 { + list.push(i).unwrap(); + } + + assert_eq!(list.is_empty(), false); + assert!(list.is_full()); + assert_eq!(list.peek(), Some(&8)); + + list.pop(); + list.pop(); + list.push(100).unwrap(); + + assert_eq!(list.peek(), Some(&100)); + + list.zeroize(); + + assert_eq!(list.peek(), None); + assert!(list.is_empty()); + + unsafe { + for node in &list.list.buffer { + assert_eq!(node.val.assume_init(), 0); + } + } + } + fn _test_variance<'a: 'b, 'b>( x: SortedLinkedList<&'a (), Max, 42, u8>, ) -> SortedLinkedList<&'b (), Max, 42, u8> { diff --git a/src/string/mod.rs b/src/string/mod.rs index b7f07712..207d35c6 100644 --- a/src/string/mod.rs +++ b/src/string/mod.rs @@ -11,6 +11,9 @@ use core::{ str::{self, Utf8Error}, }; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + use crate::CapacityError; use crate::{ len_type::LenType, @@ -142,6 +145,7 @@ pub type ViewStorage = ViewVecStorage; /// /// In most cases you should use [`String`] or [`StringView`] directly. Only use this /// struct if you want to write code that's generic over both. +#[cfg_attr(feature = "zeroize", derive(Zeroize), zeroize(bound = "S: Zeroize"))] pub struct StringInner { vec: VecInner, } @@ -1462,4 +1466,26 @@ mod tests { let mut s: String<8> = String::try_from("a").unwrap(); _ = s.insert_str(2, "a"); } + + #[test] + #[cfg(feature = "zeroize")] + fn test_string_zeroize() { + use zeroize::Zeroize; + + let mut s: String<32> = String::try_from("sensitive_password").unwrap(); + + assert_eq!(s.as_str(), "sensitive_password"); + assert!(!s.is_empty()); + assert_eq!(s.len(), 18); + + s.truncate(9); + assert_eq!(s.as_str(), "sensitive"); + assert_eq!(s.len(), 9); + + // zeroized using Vec's implementation + s.zeroize(); + + assert_eq!(s.len(), 0); + assert!(s.is_empty()); + } } diff --git a/src/vec/mod.rs b/src/vec/mod.rs index 52a5f830..5e916dfb 100644 --- a/src/vec/mod.rs +++ b/src/vec/mod.rs @@ -11,6 +11,9 @@ use core::{ slice, }; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + use crate::len_type::{check_capacity_fits, LenType}; use crate::CapacityError; @@ -84,7 +87,11 @@ mod storage { Self: VecStorage; } + #[cfg(feature = "zeroize")] + use zeroize::Zeroize; + // One sealed layer of indirection to hide the internal details (The MaybeUninit). + #[cfg_attr(feature = "zeroize", derive(Zeroize))] pub struct VecStorageInner { pub(crate) buffer: T, } @@ -208,6 +215,7 @@ pub use drain::Drain; /// /// In most cases you should use [`Vec`] or [`VecView`] directly. Only use this /// struct if you want to write code that's generic over both. +#[cfg_attr(feature = "zeroize", derive(Zeroize), zeroize(bound = "S: Zeroize"))] pub struct VecInner + ?Sized> { phantom: PhantomData, len: LenT, @@ -2247,6 +2255,69 @@ mod tests { as TryInto>>::try_into(av.clone()).unwrap_err(); } + #[test] + #[cfg(feature = "zeroize")] + fn test_vec_zeroize() { + use zeroize::Zeroize; + + let mut v: Vec = Vec::new(); + for i in 0..8 { + v.push(i).unwrap(); + } + + for i in 0..8 { + assert_eq!(v[i], i as u8); + } + + v.truncate(4); + assert_eq!(v.len(), 4); + + for i in 0..4 { + assert_eq!(v[i], i as u8); + } + + v.zeroize(); + + assert_eq!(v.len(), 0); + + unsafe { + v.set_len(8); + } + + for i in 0..8 { + assert_eq!(v[i], 0); + } + } + + #[test] + #[cfg(feature = "zeroize")] + fn test_vecview_zeroize() { + use zeroize::Zeroize; + + let mut v: Vec = Vec::new(); + for i in 0..8 { + v.push(i).unwrap(); + } + + let view = v.as_mut_view(); + + for i in 0..8 { + assert_eq!(view[i], i as u8); + } + + view.zeroize(); + + assert_eq!(view.len(), 0); + + unsafe { + view.set_len(8); + } + + for i in 0..8 { + assert_eq!(view[i], 0); + } + } + fn _test_variance<'a: 'b, 'b>(x: Vec<&'a (), 42>) -> Vec<&'b (), 42> { x }