diff --git a/Cargo.toml b/Cargo.toml index b19b93e..93f0aa3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ documentation = "https://docs.rs/smallvec/" [features] const_generics = [] +const_new = ["const_generics"] write = [] union = [] specialization = [] @@ -23,3 +24,7 @@ serde = { version = "1", optional = true, default-features = false } [dev_dependencies] bincode = "1.0.1" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/src/lib.rs b/src/lib.rs index 2e22a18..ba9cd78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,14 @@ //! When this feature is enabled, `SmallVec` works with any arrays of any size, not just a fixed //! list of sizes. //! +//! ### `const_new` +//! +//! **This feature requires Rust 1.51.** +//! +//! This feature exposes the functions [`SmallVec::new_const`], [`SmallVec::from_const`], and [`smallvec_inline`] which enables the `SmallVec` to be initialized from a const context. +//! For details, see the +//! [Rust Reference](https://doc.rust-lang.org/reference/const_eval.html#const-functions). +//! //! ### `specialization` //! //! **This feature is unstable and requires a nightly build of the Rust toolchain.** @@ -69,6 +77,7 @@ //! Tracking issue: [rust-lang/rust#34761](https://github.com/rust-lang/rust/issues/34761) #![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(feature = "specialization", allow(incomplete_features))] #![cfg_attr(feature = "specialization", feature(specialization))] #![cfg_attr(feature = "may_dangle", feature(dropck_eyepatch))] @@ -170,6 +179,52 @@ macro_rules! smallvec { }); } +/// Creates an inline [`SmallVec`] containing the arguments. This macro is enabled by the feature `const_new`. +/// +/// `smallvec_inline!` allows `SmallVec`s to be defined with the same syntax as array expressions in `const` contexts. +/// The inline storage `A` will always be an array of the size specified by the arguments. +/// There are two forms of this macro: +/// +/// - Create a [`SmallVec`] containing a given list of elements: +/// +/// ``` +/// # #[macro_use] extern crate smallvec; +/// # use smallvec::SmallVec; +/// # fn main() { +/// const V: SmallVec<[i32; 3]> = smallvec_inline![1, 2, 3]; +/// assert_eq!(V[0], 1); +/// assert_eq!(V[1], 2); +/// assert_eq!(V[2], 3); +/// # } +/// ``` +/// +/// - Create a [`SmallVec`] from a given element and size: +/// +/// ``` +/// # #[macro_use] extern crate smallvec; +/// # use smallvec::SmallVec; +/// # fn main() { +/// const V: SmallVec<[i32; 3]> = smallvec_inline![1; 3]; +/// assert_eq!(V, SmallVec::from_buf([1, 1, 1])); +/// # } +/// ``` +/// +/// Note that the behavior mimics that of array expressions, in contrast to [`smallvec`]. +#[cfg(feature = "const_new")] +#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] +#[macro_export] +macro_rules! smallvec_inline { + // count helper: transform any expression into 1 + (@one $x:expr) => (1usize); + ($elem:expr; $n:expr) => ({ + $crate::SmallVec::<[_; $n]>::from_const([$elem; $n]) + }); + ($($x:expr),+ $(,)?) => ({ + const N: usize = 0usize $(+ $crate::smallvec_inline!(@one $x))*; + $crate::SmallVec::<[_; N]>::from_const([$($x,)*]) + }); +} + /// `panic!()` in debug builds, optimization hint in release. #[cfg(not(feature = "union"))] macro_rules! debug_unreachable { @@ -353,6 +408,17 @@ union SmallVecData { heap: (*mut A::Item, usize), } +#[cfg(all(feature = "union", feature = "const_new"))] +impl SmallVecData<[T; N]> { + #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] + #[inline] + const fn from_const(inline: MaybeUninit<[T; N]>) -> Self { + SmallVecData { + inline: core::mem::ManuallyDrop::new(inline), + } + } +} + #[cfg(feature = "union")] impl SmallVecData { #[inline] @@ -393,6 +459,15 @@ enum SmallVecData { Heap((*mut A::Item, usize)), } +#[cfg(all(not(feature = "union"), feature = "const_new"))] +impl SmallVecData<[T; N]> { + #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] + #[inline] + const fn from_const(inline: MaybeUninit<[T; N]>) -> Self { + SmallVecData::Inline(inline) + } +} + #[cfg(not(feature = "union"))] impl SmallVecData { #[inline] @@ -1487,6 +1562,7 @@ impl BorrowMut<[A::Item]> for SmallVec { } #[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(feature = "write")))] impl> io::Write for SmallVec { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { @@ -1507,6 +1583,7 @@ impl> io::Write for SmallVec { } #[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl Serialize for SmallVec where A::Item: Serialize, @@ -1521,6 +1598,7 @@ where } #[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl<'de, A: Array> Deserialize<'de> for SmallVec where A::Item: Deserialize<'de>, @@ -1950,7 +2028,35 @@ impl<'a> Drop for SetLenOnDrop<'a> { } } -#[cfg(feature = "const_generics")] +#[cfg(feature = "const_new")] +impl SmallVec<[T; N]> { + /// Construct an empty vector. + /// + /// This is a `const` version of [`SmallVec::new`] that is enabled by the feature `const_new`, with the limitation that it only works for arrays. + #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] + #[inline] + pub const fn new_const() -> Self { + SmallVec { + capacity: 0, + data: SmallVecData::from_const(MaybeUninit::uninit()), + } + } + + /// The array passed as an argument is moved to be an inline version of `SmallVec`. + /// + /// This is a `const` version of [`SmallVec::from_buf`] that is enabled by the feature `const_new`, with the limitation that it only works for arrays. + #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] + #[inline] + pub const fn from_const(items: [T; N]) -> Self { + SmallVec { + capacity: N, + data: SmallVecData::from_const(MaybeUninit::new(items)), + } + } +} + +#[cfg(all(feature = "const_generics", not(doc)))] +#[cfg_attr(docsrs, doc(cfg(feature = "const_generics")))] unsafe impl Array for [T; N] { type Item = T; fn size() -> usize { @@ -1958,7 +2064,7 @@ unsafe impl Array for [T; N] { } } -#[cfg(not(feature = "const_generics"))] +#[cfg(any(not(feature = "const_generics"), doc))] macro_rules! impl_array( ($($size:expr),+) => { $( @@ -1970,7 +2076,7 @@ macro_rules! impl_array( } ); -#[cfg(not(feature = "const_generics"))] +#[cfg(any(not(feature = "const_generics"), doc))] impl_array!( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 36, 0x40, 0x60, 0x80, 0x100, 0x200, 0x400, 0x600, 0x800, 0x1000, diff --git a/src/tests.rs b/src/tests.rs index f30dfa2..bc4f3f6 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -903,6 +903,35 @@ fn const_generics() { let _v = SmallVec::<[i32; 987]>::default(); } +#[cfg(feature = "const_new")] +#[test] +fn const_new() { + let v = const_new_inner(); + assert_eq!(v.capacity(), 4); + assert_eq!(v.len(), 0); + let v = const_new_inline_sized(); + assert_eq!(v.capacity(), 4); + assert_eq!(v.len(), 4); + assert_eq!(v[0], 1); + let v = const_new_inline_args(); + assert_eq!(v.capacity(), 2); + assert_eq!(v.len(), 2); + assert_eq!(v[0], 1); + assert_eq!(v[1], 4); +} +#[cfg(feature = "const_new")] +const fn const_new_inner() -> SmallVec<[i32; 4]> { + SmallVec::<[i32; 4]>::new_const() +} +#[cfg(feature = "const_new")] +const fn const_new_inline_sized() -> SmallVec<[i32; 4]> { + crate::smallvec_inline![1; 4] +} +#[cfg(feature = "const_new")] +const fn const_new_inline_args() -> SmallVec<[i32; 2]> { + crate::smallvec_inline![1, 4] +} + #[test] fn empty_macro() { let _v: SmallVec<[u8; 1]> = smallvec![];