diff --git a/Cargo.toml b/Cargo.toml index ff39340..93f0aa3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/smallvec/" [features] const_generics = [] -const_new = [] +const_new = ["const_generics"] write = [] union = [] specialization = [] @@ -24,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 8cbc180..1295896 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,13 +72,14 @@ //! //! **This feature is unstable and requires a nightly build of the Rust toolchain.** //! -//! This feature exposes the function [`SmallVec::new_const`] which is a `const fn` so the `SmallVec` may be used from a const context. +//! This feature exposes the functions [`SmallVec::new_const`] and [`SmallVec::from_const`] 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). //! //! Tracking issue: [rust-lang/rust#57563](https://github.com/rust-lang/rust/issues/57563) #![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))] @@ -181,6 +182,53 @@ 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 { @@ -366,6 +414,7 @@ union SmallVecData { #[cfg(all(feature = "union", feature = "const_new"))] impl SmallVecData { + #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] #[inline] const fn from_const(inline: MaybeUninit) -> SmallVecData { SmallVecData { @@ -416,6 +465,7 @@ enum SmallVecData { #[cfg(all(not(feature = "union"), feature = "const_new"))] impl SmallVecData { + #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] #[inline] const fn from_const(inline: MaybeUninit) -> SmallVecData { SmallVecData::Inline(inline) @@ -1360,7 +1410,7 @@ impl SmallVec { #[cfg(feature = "const_new")] impl SmallVec { - /// Construct an empty vector. + /// Construct an empty vector. This is currently gated behind the feature `const_new`. /// /// # Safety /// No size validation is attempted for this function. @@ -1369,6 +1419,7 @@ impl SmallVec { /// /// [`Array`]: crate::Array /// [`new`]: crate::SmallVec::new + #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] #[inline] pub const unsafe fn new_const() -> SmallVec { SmallVec { @@ -1536,6 +1587,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 { @@ -1556,6 +1608,7 @@ impl> io::Write for SmallVec { } #[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl Serialize for SmallVec where A::Item: Serialize, @@ -1570,6 +1623,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>, @@ -1999,7 +2053,22 @@ impl<'a> Drop for SetLenOnDrop<'a> { } } -#[cfg(feature = "const_generics")] +#[cfg(feature = "const_new")] +impl SmallVec<[T; N]> { + /// 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]) -> SmallVec<[T; N]> { + 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 { @@ -2007,7 +2076,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),+) => { $( @@ -2019,7 +2088,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 a7c3fa6..1c2c2e6 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -906,12 +906,31 @@ fn const_generics() { #[cfg(feature = "const_new")] #[test] fn const_new() { - let _v = const_new_inner(); + 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]> { unsafe { 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() {