diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 94379afc2bd45..448ad5909e0b2 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -123,6 +123,7 @@ #![feature(alloc_layout_extra)] #![feature(try_trait)] #![feature(associated_type_bounds)] +#![feature(recycle_vec)] // Allow testing this library diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 3273feb7b5dd4..55ac639cc6441 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -10,6 +10,7 @@ #![feature(associated_type_bounds)] #![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_drain_sorted)] +#![feature(recycle_vec)] use std::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index 80537217697ad..aea7fc6a4ab1c 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -1264,6 +1264,93 @@ fn test_try_reserve_exact() { } +/// Tests that `recycle` successfully re-interprets the type +/// to have a different lifetime from the original +#[test] +fn test_recycle_lifetime() { + let s_1 = "foo".to_string(); + + let val_size; + let val_align; + + let mut buf = Vec::with_capacity(100); + { + let mut buf2 = buf; + let s_2 = "bar".to_string(); + buf2.push(s_2.as_str()); + + assert_eq!(buf2.len(), 1); + assert_eq!(buf2.capacity(), 100); + val_size = core::mem::size_of_val(&buf2[0]); + val_align = core::mem::align_of_val(&buf2[0]); + + buf = buf2.recycle(); + } + buf.push(s_1.as_str()); + assert_eq!(val_size, core::mem::size_of_val(&buf[0])); + assert_eq!(val_align, core::mem::align_of_val(&buf[0])); +} + +/// Tests that `recycle` successfully re-interprets the type itself +#[test] +fn test_recycle_type() { + let s = "foo".to_string(); + + let val_size; + let val_align; + + let mut buf = Vec::with_capacity(100); + { + let mut buf2 = buf.recycle(); + + let mut i = Vec::new(); + i.push(1); + i.push(2); + i.push(3); + + buf2.push(i.as_slice()); + + assert_eq!(buf2.len(), 1); + assert_eq!(buf2.capacity(), 100); + val_size = core::mem::size_of_val(&buf2[0]); + val_align = core::mem::align_of_val(&buf2[0]); + + buf = buf2.recycle(); + } + buf.push(s.as_str()); + assert_eq!(val_size, core::mem::size_of_val(&buf[0])); + assert_eq!(val_align, core::mem::align_of_val(&buf[0])); +} + +/// Tests that `recycle` successfully panics with incompatible sizes +#[test] +#[should_panic] +fn test_recycle_incompatible_size() { + let mut buf = Vec::with_capacity(100); + buf.push(1_u16); + { + let mut buf2 = buf.recycle(); + buf2.push(1_u32); + buf = buf2.recycle(); + } + buf.push(1_u16); +} + + +/// Tests that `recycle` successfully panics with incompatible alignments +#[test] +#[should_panic] +fn test_recycle_incompatible_alignment() { + let mut buf = Vec::with_capacity(100); + buf.push([0_u16, 1_u16]); + { + let mut buf2 = buf.recycle(); + buf2.push(1_u32); + buf = buf2.recycle(); + } + buf.push([0_u16, 1_u16]); +} + #[test] fn test_stable_push_pop() { // Test that, if we reserved enough space, adding and removing elements does not diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 5b53a6a289958..cd1320a537999 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! A contiguous growable array type with heap-allocated contents, written //! `Vec`. //! @@ -603,6 +604,92 @@ impl Vec { self.buf.try_reserve_exact(self.len, additional) } + /// Allows reusing the allocation of the `Vec` at a different, + /// but compatible, type `Vec`. + /// The vector is emptied and any values contained in it will be dropped. + /// As a result, no elements of type `T` are transmuted to `U` + /// and so this operation is safe. + /// The target type must have the same size and alignment as the source type. + /// + /// # Panics + /// Panics if the size or alignment of the source and target types don't match. + /// + /// # Example + /// + /// This API is useful especially when using `Vec` as a + /// temporary storage for data with short lifetimes. + /// By recycling the allocation, the `Vec` is able to safely + /// outlive the lifetime of the type that was stored in it. + /// ``` + /// #![feature(recycle_vec)] + /// # use std::error::Error; + /// # + /// # struct Stream(bool); + /// # + /// # impl Stream { + /// # fn new() -> Self { + /// # Stream(false) + /// # } + /// # + /// # fn next(&mut self) -> Option<&[u8]> { + /// # if self.0 { + /// # None + /// # } else { + /// # self.0 = true; + /// # Some(&b"foo"[..]) + /// # } + /// # } + /// # } + /// # + /// # fn process(input: &[Object<'_>]) -> Result<(), Box> { + /// # Ok(()) + /// # } + /// # + /// # struct Object<'a> { + /// # #[allow(dead_code)] + /// # reference: &'a [u8], + /// # } + /// # + /// # fn deserialize<'a>( + /// # input: &'a [u8], + /// # output: &mut Vec>, + /// # ) -> Result<(), Box> { + /// # output.push(Object { reference: input }); + /// # Ok(()) + /// # } + /// # + /// # fn main() -> Result<(), Box> { + /// # let mut stream = Stream::new(); + /// let mut objects: Vec> = Vec::new(); // Any lifetime goes here + /// + /// while let Some(byte_chunk) = stream.next() { // `byte_chunk` lifetime starts + /// let mut temp: Vec> = objects.recycle(); // `temp` lifetime starts + /// + /// // Zero-copy parsing; deserialized `Object`s have references to `byte_chunk`. + /// deserialize(byte_chunk, &mut temp)?; + /// process(&temp)?; + /// + /// objects = temp.recycle(); // `temp` lifetime ends + /// } // `byte_chunk` lifetime ends + /// # Ok(()) + /// # } + /// ``` + /// + /// # Note about stabilization + /// The size and alignment contract is enforceable at compile-time, + /// so we will want to wait until compile-time asserts become stable and + /// modify this API to cause a compile error instead of panicking + /// before stabilizing it. + #[unstable(feature = "recycle_vec", reason = "new API", issue = "0")] + pub fn recycle(mut self) -> Vec { + assert_eq!(core::mem::size_of::(), core::mem::size_of::()); + assert_eq!(core::mem::align_of::(), core::mem::align_of::()); + self.clear(); + let (ptr, _, capacity) = self.into_raw_parts(); + let ptr = ptr as *mut U; + unsafe { Vec::from_raw_parts(ptr, 0, capacity) } + } + /// Shrinks the capacity of the vector as much as possible. /// /// It will drop down as close as possible to the length but the allocator