diff --git a/src/arrayvec.rs b/src/arrayvec.rs index e5ea52dc..bd08e1c5 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -1137,6 +1137,11 @@ impl ArrayVec { debug_assert_ne!(ptr, end_ptr); if mem::size_of::() != 0 { ptr.write(elt); + } else { + // The ZST element has logically been moved into the vector. + // There is no memory to write, but dropping `elt` here would + // drop it once now and once again when the vector is dropped. + mem::forget(elt); } ptr = raw_ptr_add(ptr, 1); guard.data += 1; diff --git a/tests/tests.rs b/tests/tests.rs index ff779baa..098614b2 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -716,6 +716,32 @@ fn test_extend_zst() { assert_eq!(array.len(), 5); } +#[test] +fn test_extend_zst_with_drop_is_not_dropped_twice_via_safe_api() { + use std::sync::atomic::{AtomicUsize, Ordering}; + + static DROP_COUNT: AtomicUsize = AtomicUsize::new(0); + + struct ZstWithSafeDrop; + + impl Drop for ZstWithSafeDrop { + fn drop(&mut self) { + let previous = DROP_COUNT.fetch_add(1, Ordering::SeqCst); + // Extending with a single ZST moves one logical value into the ArrayVec. + // Its Drop implementation must run exactly once, when the ArrayVec drops. + if previous != 0 { + panic!("ZST value dropped more than once"); + } + } + } + + DROP_COUNT.store(0, Ordering::SeqCst); + + let mut vec = ArrayVec::::new(); + vec.extend(std::iter::once(ZstWithSafeDrop)); + drop(vec); +} + #[test] fn test_try_from_argument() { use core::convert::TryFrom;