From dcab4fc13b3715abb6c45dc10d9deaf1f9ecd3f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 15 Sep 2026 19:09:09 +0200 Subject: [PATCH 1/3] lib: Make const_new the default, and bump MSRV. Make the feature do nothing, to avoid breaking dependent crates. --- Cargo.toml | 2 +- src/lib.rs | 63 +++++++++++++++++------------------------------------- 2 files changed, 21 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 377f226..c55d293 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "thin-vec" version = "0.2.19" authors = ["Aria Beingessner "] edition = "2018" -rust-version = "1.53" +rust-version = "1.83" description = "A Vec that takes up less space on the stack." readme = "README.md" homepage = "https://github.com/mozilla/thin-vec" diff --git a/src/lib.rs b/src/lib.rs index 390cd9a..eaa5b95 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,13 +24,6 @@ //! //! # Optional Features //! -//! ## `const_new` -//! -//! **This feature requires Rust 1.83.** -//! -//! This feature makes `ThinVec::new()` a `const fn`. -//! -//! //! # Gecko FFI //! //! If you enable the gecko-ffi feature, `ThinVec` will verbatim bridge with the nsTArray type in @@ -411,26 +404,24 @@ fn alloc_size(cap: usize) -> usize { } /// Gets the padding necessary for the array of a `ThinVec` -fn padding() -> usize { +const fn padding() -> usize { let alloc_align = alloc_align::(); let header_size = mem::size_of::
(); - - if alloc_align > header_size { - if cfg!(feature = "gecko-ffi") { - panic!( - "nsTArray does not handle alignment above > {} correctly", - header_size - ); - } - alloc_align - header_size - } else { - 0 + if cfg!(feature = "gecko-ffi") { + assert!( + header_size >= alloc_align, + "nsTArray does not handle alignment above the header size correctly", + ); } + alloc_align.saturating_sub(header_size) } /// Gets the align necessary to allocate a `ThinVec` -fn alloc_align() -> usize { - max(mem::align_of::(), mem::align_of::
()) +const fn alloc_align() -> usize { + if mem::align_of::() > mem::align_of::
() { + return mem::align_of::(); + } + mem::align_of::
() } /// Gets the layout necessary to allocate a `ThinVec` @@ -527,16 +518,9 @@ impl ThinVec { /// Creates a new empty ThinVec. /// /// This will not allocate. - #[cfg(not(feature = "const_new"))] - pub fn new() -> ThinVec { - ThinVec::with_capacity(0) - } - - /// Creates a new empty ThinVec. - /// - /// This will not allocate. - #[cfg(feature = "const_new")] pub const fn new() -> ThinVec { + // See the comment in with_capacity(). + let _ = padding::(); unsafe { ThinVec { ptr: NonNull::new_unchecked(&EMPTY_HEADER as *const Header as *mut Header), @@ -599,7 +583,7 @@ impl ThinVec { /// // Only true **without** the gecko-ffi feature! /// // assert_eq!(vec_units.capacity(), usize::MAX); /// ``` - pub fn with_capacity(cap: usize) -> ThinVec { + pub fn with_capacity(cap: usize) -> Self { // `padding` contains ~static assertions against types that are // incompatible with the current feature flags. We also call it to // invoke these assertions when getting a pointer to the `ThinVec` @@ -608,19 +592,12 @@ impl ThinVec { // double panic. We duplicate the assertion here so that it is // testable, let _ = padding::(); - if cap == 0 { - unsafe { - ThinVec { - ptr: NonNull::new_unchecked(&EMPTY_HEADER as *const Header as *mut Header), - boo: PhantomData, - } - } - } else { - ThinVec { - ptr: header_with_capacity::(cap, false), - boo: PhantomData, - } + return Self::new(); + } + ThinVec { + ptr: header_with_capacity::(cap, false), + boo: PhantomData, } } From ded026a4d23575ede7b98ef6978fbbb51ba39ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 15 Sep 2026 19:09:37 +0200 Subject: [PATCH 2/3] chore: Bump edition to Rust 2024. --- Cargo.toml | 4 ++-- src/lib.rs | 69 +++++++++++++++++++++++++++--------------------------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c55d293..6434f01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,8 @@ name = "thin-vec" version = "0.2.19" authors = ["Aria Beingessner "] -edition = "2018" -rust-version = "1.83" +edition = "2024" +rust-version = "1.85" description = "A Vec that takes up less space on the stack." readme = "README.md" homepage = "https://github.com/mozilla/thin-vec" diff --git a/src/lib.rs b/src/lib.rs index eaa5b95..bc001a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -655,7 +655,7 @@ impl ThinVec { // This is unsafe when the header is EMPTY_HEADER. unsafe fn header_mut(&mut self) -> &mut Header { - &mut *self.ptr() + unsafe { &mut *self.ptr() } } /// Returns the number of elements in the vector, also referred to @@ -741,7 +741,7 @@ impl ThinVec { /// # // don't use this as a starting point for a real library. /// # pub struct StreamWrapper { strm: *mut std::ffi::c_void } /// # const Z_OK: i32 = 0; - /// # extern "C" { + /// # unsafe extern "C" { /// # fn deflateGetDictionary( /// # strm: *mut std::ffi::c_void, /// # dictionary: *mut u8, @@ -797,13 +797,14 @@ impl ThinVec { // less than or equal to capacity(). The same applies here. debug_assert!(len == 0, "invalid set_len({}) on empty ThinVec", len); } else { - self.header_mut().set_len(len) + unsafe { self.set_len_non_singleton(len) } } } // For internal use only, when setting the length and it's known to be the non-singleton. + #[inline] unsafe fn set_len_non_singleton(&mut self, len: usize) { - self.header_mut().set_len(len) + unsafe { self.header_mut().set_len(len) } } /// Appends an element to the back of a collection. @@ -1126,11 +1127,7 @@ impl ThinVec { // Ensure the new capacity is at least double, to guarantee exponential growth. let double_cap = if old_cap == 0 { // skip to 4 because tiny ThinVecs are dumb; but not if that would cause overflow - if mem::size_of::() > (!0) / 8 { - 1 - } else { - 4 - } + if mem::size_of::() > (!0) / 8 { 1 } else { 4 } } else { old_cap.saturating_mul(2) }; @@ -1724,17 +1721,18 @@ impl ThinVec { debug_assert!(new_cap > 0); if self.has_allocation() { let old_cap = self.capacity(); - let ptr = realloc( - self.ptr() as *mut u8, - layout::(old_cap), - alloc_size::(new_cap), - ) as *mut Header; - - if ptr.is_null() { - handle_alloc_error(layout::(new_cap)) + unsafe { + let ptr = realloc( + self.ptr() as *mut u8, + layout::(old_cap), + alloc_size::(new_cap), + ) as *mut Header; + if ptr.is_null() { + handle_alloc_error(layout::(new_cap)) + } + (*ptr).set_cap_and_auto(new_cap, (*ptr).is_auto()); + self.ptr = NonNull::new_unchecked(ptr); } - (*ptr).set_cap_and_auto(new_cap, (*ptr).is_auto()); - self.ptr = NonNull::new_unchecked(ptr); } else { let mut new_header = header_with_capacity::(new_cap, self.is_auto_array()); @@ -1751,13 +1749,15 @@ impl ThinVec { // by leaving behind a valid empty instance. let len = self.len(); if cfg!(feature = "gecko-ffi") && len > 0 { - new_header - .as_ptr() - .add(1) - .cast::() - .copy_from_nonoverlapping(self.data_raw(), len); - self.set_len_non_singleton(0); - new_header.as_mut().set_len(len); + unsafe { + new_header + .as_ptr() + .add(1) + .cast::() + .copy_from_nonoverlapping(self.data_raw(), len); + self.set_len_non_singleton(0); + new_header.as_mut().set_len(len); + } } self.ptr = new_header; @@ -2087,8 +2087,8 @@ impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for ThinVec { where D: serde::Deserializer<'de>, { - use serde::de::{SeqAccess, Visitor}; use serde::Deserialize; + use serde::de::{SeqAccess, Visitor}; struct ThinVecVisitor(PhantomData); @@ -2902,7 +2902,7 @@ impl AutoThinVec { let this = unsafe { self.get_unchecked_mut() }; this.buffer.header.set_len(0); // TODO(emilio): Use NonNull::from_mut when msrv allows. - this.inner.ptr = NonNull::new_unchecked(&mut this.buffer.header); + this.inner.ptr = unsafe { NonNull::new_unchecked(&mut this.buffer.header) }; debug_assert!(this.inner.is_auto_array()); debug_assert!(this.inner.uses_stack_allocated_buffer()); } @@ -2953,11 +2953,12 @@ impl Drain<'_, T> { }; for place in range_slice { - if let Some(new_item) = replace_with.next() { - unsafe { ptr::write(place, new_item) }; - vec.set_len(vec.len() + 1); - } else { + let Some(new_item) = replace_with.next() else { return false; + }; + unsafe { + ptr::write(place, new_item); + vec.set_len(vec.len() + 1); } } true @@ -3079,7 +3080,7 @@ impl std::io::Write for ThinVec { #[cfg(test)] mod tests { - use super::{ThinVec, MAX_CAP}; + use super::{MAX_CAP, ThinVec}; use crate::alloc::{string::ToString, vec}; #[test] @@ -4713,7 +4714,7 @@ mod std_tests { } #[cfg(feature = "serde")] - use serde_test::{assert_tokens, Token}; + use serde_test::{Token, assert_tokens}; #[test] #[cfg(feature = "serde")] From 29b5d8c1f1db8641b25fb1d30080985a8c31a0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 15 Sep 2026 19:27:21 +0200 Subject: [PATCH 3/3] chore: Document some of the recent changes. --- RELEASES.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 46dff10..2fe0b7e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,13 @@ +# Unreleased + + * Make const_new the default, and bump MSRV to 1.85. `const_new` feature is still available but does nothing. + +# Version 0.2.19 (2026-07-26) + + * add may_dangle Drop impl under unstable feature + * Use safe `Layout::from_size_align` (instead of unsafe `from_size_align_unchecked`), panicking with a capacity overflow if it fails. + * Fix `shallow_size_of` for inline arrays. + # Versions 0.2.17 and 0.2.18 (2026-04-29) * Fix compiling some feature combinations in no_std mode