diff --git a/src/array_string.rs b/src/array_string.rs index 90cfc09..0a8a397 100644 --- a/src/array_string.rs +++ b/src/array_string.rs @@ -80,6 +80,41 @@ impl ArrayString ArrayString { xs: MakeMaybeUninit::ARRAY, len: 0 } } + /// Create a new empty `ArrayString` directly on the heap. + /// + /// Capacity is inferred from the type parameter. + /// + /// ``` + /// use arrayvec::ArrayString; + /// + /// let mut string = ArrayString::<16>::boxed(); + /// string.push_str("foo"); + /// assert_eq!(&string[..], "foo"); + /// assert_eq!(string.capacity(), 16); + /// ``` + #[track_caller] + pub fn boxed() -> Box { + assert_capacity_limit!(CAP); + let layout = std::alloc::Layout::new::(); + // SAFETY: `Self` is not a ZST, it contains at least `len`, even if + // `CAP` is 0 + let ptr = unsafe { std::alloc::alloc(layout) as *mut Self }; + if ptr.is_null() { + std::alloc::handle_alloc_error(layout); + } + // SAFETY: + // - `len` needs to be initialized to 0 + // - `xs` does _not_ need to be initialized if `len` is 0. + // - `ptr` is valid and well aligned for type `Self` + unsafe { std::ptr::addr_of_mut!((*ptr).len).write(0) }; + + // SAFETY: "It is valid to convert both ways between a Box and a raw + // pointer allocated with the Global allocator, given that the Layout + // used with the allocator is correct for the type." + // https://doc.rust-lang.org/stable/std/boxed/index.html#memory-layout + unsafe { Box::from_raw(ptr) } + } + /// Return the length of the string. #[inline] pub const fn len(&self) -> usize { self.len as usize } diff --git a/src/arrayvec.rs b/src/arrayvec.rs index 37e151a..7fe881b 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -100,6 +100,42 @@ impl ArrayVec { ArrayVec { xs: MakeMaybeUninit::ARRAY, len: 0 } } + /// Create a new empty `ArrayVec` directly on the heap. + /// + /// The maximum capacity is given by the generic parameter `CAP`. + /// + /// ``` + /// use arrayvec::ArrayVec; + /// + /// let mut array = ArrayVec::<_, 16>::boxed(); + /// array.push(1); + /// array.push(2); + /// assert_eq!(&array[..], &[1, 2]); + /// assert_eq!(array.capacity(), 16); + /// ``` + #[track_caller] + pub fn boxed() -> Box { + assert_capacity_limit!(CAP); + let layout = std::alloc::Layout::new::(); + // SAFETY: `Self` is not a ZST, it contains at least `len`, even if `T` + // is a ZST or `CAP` is 0 + let ptr = unsafe { std::alloc::alloc(layout) as *mut Self }; + if ptr.is_null() { + std::alloc::handle_alloc_error(layout); + } + // SAFETY: + // - `len` needs to be initialized to 0 + // - `xs` does _not_ need to be initialized if `len` is 0. + // - `ptr` is valid and well aligned for type `Self` + unsafe { std::ptr::addr_of_mut!((*ptr).len).write(0) }; + + // SAFETY: "It is valid to convert both ways between a Box and a raw + // pointer allocated with the Global allocator, given that the Layout + // used with the allocator is correct for the type." + // https://doc.rust-lang.org/stable/std/boxed/index.html#memory-layout + unsafe { Box::from_raw(ptr) } + } + /// Return the number of elements in the `ArrayVec`. /// /// ``` diff --git a/tests/tests.rs b/tests/tests.rs index 2f8a5ef..4bc1e48 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -790,4 +790,52 @@ fn test_arraystring_zero_filled_has_some_sanity_checks() { let string = ArrayString::<4>::zero_filled(); assert_eq!(string.as_str(), "\0\0\0\0"); assert_eq!(string.len(), 4); -} \ No newline at end of file +} + +#[test] +fn test_arrayvec_heap_constructible() { + let mut var: Box, 10>> = ArrayVec::boxed(); + + assert!(var.is_empty()); + var.push(vec![3, 5, 8]); + assert_eq!(var[..], [vec![3, 5, 8]]); +} + +#[test] +fn test_arraystring_heap_constructible() { + let mut var: Box> = ArrayString::boxed(); + + assert!(var.is_empty()); + var.push_str("hello"); + assert_eq!(*var, *"hello"); +} + +#[test] +fn test_arrayvec_heap_zero_capacity() { + let mut var: Box> = ArrayVec::boxed(); + + assert!(var.is_empty()); + assert!(var.try_push(0).is_err()); +} + +#[test] +fn test_arraystring_heap_zero_capacity() { + let mut var: Box> = ArrayString::boxed(); + + assert!(var.is_empty()); + assert!(var.try_push_str("hello").is_err()); +} + +#[test] +fn test_arrayvec_heap_zst() { + #[derive(Copy, Clone, PartialEq, Debug)] + struct Z; // Zero sized type + + let mut var: Box> = ArrayVec::boxed(); + + for _ in 0..5 { + var.push(Z); + } + assert_eq!(&var[..], &[Z; 5]); + assert_eq!(var.len(), 5); +}