diff --git a/Cargo.toml b/Cargo.toml index 9e2bb83..919c243 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,6 @@ keywords = ["stack", "vector", "array", "container", "data-structure"] [dependencies.odds] version = "0.2" -[dev-dependencies] -nodrop = "0.1" - +[dependencies.nodrop] +version = "0.1.4" +path = "nodrop" diff --git a/src/lib.rs b/src/lib.rs index 9cb6dc0..ba4df1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ extern crate odds; +extern crate nodrop; use std::iter; use std::mem; @@ -9,13 +10,13 @@ use std::ops::{ }; use std::slice; +use nodrop::NoDrop; + // extra traits use std::borrow::{Borrow, BorrowMut}; use std::hash::{Hash, Hasher}; use std::fmt; -use odds::debug_assert_unreachable; - mod array; pub use array::Array; pub use odds::IndexRange as RangeArgument; @@ -30,13 +31,6 @@ unsafe fn new_array() -> A { mem::uninitialized() } -/// repr(u8) - Make sure the non-nullable pointer optimization does not occur! -#[repr(u8)] -enum NoDrop { - Alive(T), - Dropped, -} - /// A vector with a fixed capacity. /// /// The **ArrayVec** is a vector backed by a fixed size array. It keeps track of @@ -57,12 +51,12 @@ pub struct ArrayVec { impl Drop for ArrayVec { fn drop(&mut self) { // clear all elements - while let Some(_) = self.pop() { } - - // inhibit drop - unsafe { - ptr::write(&mut self.xs, NoDrop::Dropped); + while let Some(_) = self.pop() { } + + // NoDrop inhibits array's drop + // panic safety: NoDrop::drop will trigger on panic, so the inner + // array will not drop even after panic. } } @@ -84,7 +78,7 @@ impl ArrayVec { /// ``` pub fn new() -> ArrayVec { unsafe { - ArrayVec { xs: NoDrop::Alive(new_array()), len: Index::zero() } + ArrayVec { xs: NoDrop::new(new_array()), len: Index::zero() } } } @@ -375,31 +369,6 @@ impl DerefMut for ArrayVec { } } -impl Deref for NoDrop { - type Target = T; - - // Use type invariant, always Alive. - #[inline] - fn deref(&self) -> &T { - match *self { - NoDrop::Alive(ref inner) => inner, - _ => unsafe { debug_assert_unreachable() } - } - } -} - -impl DerefMut for NoDrop { - // Use type invariant, always Alive. - #[inline] - fn deref_mut(&mut self) -> &mut T { - match *self { - NoDrop::Alive(ref mut inner) => inner, - _ => unsafe { debug_assert_unreachable() } - } - } -} - - /// Create an **ArrayVec** from an array. /// /// ## Examples @@ -412,7 +381,7 @@ impl DerefMut for NoDrop { /// ``` impl From for ArrayVec { fn from(array: A) -> Self { - ArrayVec { xs: NoDrop::Alive(array), len: Index::from(A::capacity()) } + ArrayVec { xs: NoDrop::new(array), len: Index::from(A::capacity()) } } } @@ -524,10 +493,16 @@ impl ExactSizeIterator for IntoIter { } impl Drop for IntoIter { fn drop(&mut self) { - // exhaust iterator and clear the vector - while let Some(_) = self.next() { } + // panic safety: Set length to 0 before dropping elements. + let index = self.index.to_usize(); + let len = self.v.len(); unsafe { self.v.set_len(0); + let elements = slice::from_raw_parts(self.v.get_unchecked_mut(index), + len - index); + for elt in elements { + ptr::read(elt); + } } } } @@ -588,6 +563,8 @@ impl<'a, A: Array> Drop for Drain<'a, A> where A::Item: 'a { fn drop(&mut self) { + // len is currently 0 so panicking while dropping will not cause a double drop. + // exhaust self first while let Some(_) = self.next() { } diff --git a/tests/tests.rs b/tests/tests.rs index 60bbcde..1f7ff3d 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -130,12 +130,12 @@ fn test_compact_size() { // 4 elements size + 1 len + 1 enum tag + [1 drop flag] type ByteArray = ArrayVec<[u8; 4]>; println!("{}", mem::size_of::()); - assert!(mem::size_of::() <= 7); + assert!(mem::size_of::() <= 8); // 12 element size + 1 enum tag + 3 padding + 1 len + 1 drop flag + 2 padding type QuadArray = ArrayVec<[u32; 3]>; println!("{}", mem::size_of::()); - assert!(mem::size_of::() <= 20); + assert!(mem::size_of::() <= 24); } #[test] @@ -163,6 +163,37 @@ fn test_drain_oob() { v.drain(0..8); } +#[test] +#[should_panic] +fn test_drop_panic() { + struct DropPanic; + + impl Drop for DropPanic { + fn drop(&mut self) { + panic!("drop"); + } + } + + let mut array = ArrayVec::<[DropPanic; 1]>::new(); + array.push(DropPanic); +} + +#[test] +#[should_panic] +fn test_drop_panic_into_iter() { + struct DropPanic; + + impl Drop for DropPanic { + fn drop(&mut self) { + panic!("drop"); + } + } + + let mut array = ArrayVec::<[DropPanic; 1]>::new(); + array.push(DropPanic); + array.into_iter(); +} + #[test] fn test_insert() { let mut v = ArrayVec::from([]);