Skip to content

Commit 18b720f

Browse files
authored
Auto merge of #265 - TimLuq:const_new, r=mbrubeck
Added feature `const_new` which enables `SmallVec::new_const()` As I wasn't alone in finding a use for having a `const` version of `new` (see #263) I decided to make a PR for it. This adds a new feature flag `const_new` (nightly only) which exposes an alternative `new` function: `pub const unsafe fn new_const() -> SmallVec<A>`. Nightly is needed for the binding `A: Array`, which could be worked around if the strict requirement is dropped from the `struct` declarations and moved only to the existing `impl`s. If the maintainers wish for another solution for this I'll be happy to make a few more commits.
2 parents 2691f34 + 8320871 commit 18b720f

File tree

3 files changed

+143
-3
lines changed

3 files changed

+143
-3
lines changed

Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ documentation = "https://docs.rs/smallvec/"
1313

1414
[features]
1515
const_generics = []
16+
const_new = ["const_generics"]
1617
write = []
1718
union = []
1819
specialization = []
@@ -23,3 +24,7 @@ serde = { version = "1", optional = true, default-features = false }
2324

2425
[dev_dependencies]
2526
bincode = "1.0.1"
27+
28+
[package.metadata.docs.rs]
29+
all-features = true
30+
rustdoc-args = ["--cfg", "docsrs"]

src/lib.rs

+109-3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@
4848
//! When this feature is enabled, `SmallVec` works with any arrays of any size, not just a fixed
4949
//! list of sizes.
5050
//!
51+
//! ### `const_new`
52+
//!
53+
//! **This feature requires Rust 1.51.**
54+
//!
55+
//! 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.
56+
//! For details, see the
57+
//! [Rust Reference](https://doc.rust-lang.org/reference/const_eval.html#const-functions).
58+
//!
5159
//! ### `specialization`
5260
//!
5361
//! **This feature is unstable and requires a nightly build of the Rust toolchain.**
@@ -69,6 +77,7 @@
6977
//! Tracking issue: [rust-lang/rust#34761](https://github.com/rust-lang/rust/issues/34761)
7078
7179
#![no_std]
80+
#![cfg_attr(docsrs, feature(doc_cfg))]
7281
#![cfg_attr(feature = "specialization", allow(incomplete_features))]
7382
#![cfg_attr(feature = "specialization", feature(specialization))]
7483
#![cfg_attr(feature = "may_dangle", feature(dropck_eyepatch))]
@@ -170,6 +179,52 @@ macro_rules! smallvec {
170179
});
171180
}
172181

182+
/// Creates an inline [`SmallVec`] containing the arguments. This macro is enabled by the feature `const_new`.
183+
///
184+
/// `smallvec_inline!` allows `SmallVec`s to be defined with the same syntax as array expressions in `const` contexts.
185+
/// The inline storage `A` will always be an array of the size specified by the arguments.
186+
/// There are two forms of this macro:
187+
///
188+
/// - Create a [`SmallVec`] containing a given list of elements:
189+
///
190+
/// ```
191+
/// # #[macro_use] extern crate smallvec;
192+
/// # use smallvec::SmallVec;
193+
/// # fn main() {
194+
/// const V: SmallVec<[i32; 3]> = smallvec_inline![1, 2, 3];
195+
/// assert_eq!(V[0], 1);
196+
/// assert_eq!(V[1], 2);
197+
/// assert_eq!(V[2], 3);
198+
/// # }
199+
/// ```
200+
///
201+
/// - Create a [`SmallVec`] from a given element and size:
202+
///
203+
/// ```
204+
/// # #[macro_use] extern crate smallvec;
205+
/// # use smallvec::SmallVec;
206+
/// # fn main() {
207+
/// const V: SmallVec<[i32; 3]> = smallvec_inline![1; 3];
208+
/// assert_eq!(V, SmallVec::from_buf([1, 1, 1]));
209+
/// # }
210+
/// ```
211+
///
212+
/// Note that the behavior mimics that of array expressions, in contrast to [`smallvec`].
213+
#[cfg(feature = "const_new")]
214+
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
215+
#[macro_export]
216+
macro_rules! smallvec_inline {
217+
// count helper: transform any expression into 1
218+
(@one $x:expr) => (1usize);
219+
($elem:expr; $n:expr) => ({
220+
$crate::SmallVec::<[_; $n]>::from_const([$elem; $n])
221+
});
222+
($($x:expr),+ $(,)?) => ({
223+
const N: usize = 0usize $(+ $crate::smallvec_inline!(@one $x))*;
224+
$crate::SmallVec::<[_; N]>::from_const([$($x,)*])
225+
});
226+
}
227+
173228
/// `panic!()` in debug builds, optimization hint in release.
174229
#[cfg(not(feature = "union"))]
175230
macro_rules! debug_unreachable {
@@ -353,6 +408,17 @@ union SmallVecData<A: Array> {
353408
heap: (*mut A::Item, usize),
354409
}
355410

411+
#[cfg(all(feature = "union", feature = "const_new"))]
412+
impl<T, const N: usize> SmallVecData<[T; N]> {
413+
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
414+
#[inline]
415+
const fn from_const(inline: MaybeUninit<[T; N]>) -> Self {
416+
SmallVecData {
417+
inline: core::mem::ManuallyDrop::new(inline),
418+
}
419+
}
420+
}
421+
356422
#[cfg(feature = "union")]
357423
impl<A: Array> SmallVecData<A> {
358424
#[inline]
@@ -393,6 +459,15 @@ enum SmallVecData<A: Array> {
393459
Heap((*mut A::Item, usize)),
394460
}
395461

462+
#[cfg(all(not(feature = "union"), feature = "const_new"))]
463+
impl<T, const N: usize> SmallVecData<[T; N]> {
464+
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
465+
#[inline]
466+
const fn from_const(inline: MaybeUninit<[T; N]>) -> Self {
467+
SmallVecData::Inline(inline)
468+
}
469+
}
470+
396471
#[cfg(not(feature = "union"))]
397472
impl<A: Array> SmallVecData<A> {
398473
#[inline]
@@ -1487,6 +1562,7 @@ impl<A: Array> BorrowMut<[A::Item]> for SmallVec<A> {
14871562
}
14881563

14891564
#[cfg(feature = "write")]
1565+
#[cfg_attr(docsrs, doc(cfg(feature = "write")))]
14901566
impl<A: Array<Item = u8>> io::Write for SmallVec<A> {
14911567
#[inline]
14921568
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
@@ -1507,6 +1583,7 @@ impl<A: Array<Item = u8>> io::Write for SmallVec<A> {
15071583
}
15081584

15091585
#[cfg(feature = "serde")]
1586+
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
15101587
impl<A: Array> Serialize for SmallVec<A>
15111588
where
15121589
A::Item: Serialize,
@@ -1521,6 +1598,7 @@ where
15211598
}
15221599

15231600
#[cfg(feature = "serde")]
1601+
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
15241602
impl<'de, A: Array> Deserialize<'de> for SmallVec<A>
15251603
where
15261604
A::Item: Deserialize<'de>,
@@ -1950,15 +2028,43 @@ impl<'a> Drop for SetLenOnDrop<'a> {
19502028
}
19512029
}
19522030

1953-
#[cfg(feature = "const_generics")]
2031+
#[cfg(feature = "const_new")]
2032+
impl<T, const N: usize> SmallVec<[T; N]> {
2033+
/// Construct an empty vector.
2034+
///
2035+
/// 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.
2036+
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
2037+
#[inline]
2038+
pub const fn new_const() -> Self {
2039+
SmallVec {
2040+
capacity: 0,
2041+
data: SmallVecData::from_const(MaybeUninit::uninit()),
2042+
}
2043+
}
2044+
2045+
/// The array passed as an argument is moved to be an inline version of `SmallVec`.
2046+
///
2047+
/// 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.
2048+
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
2049+
#[inline]
2050+
pub const fn from_const(items: [T; N]) -> Self {
2051+
SmallVec {
2052+
capacity: N,
2053+
data: SmallVecData::from_const(MaybeUninit::new(items)),
2054+
}
2055+
}
2056+
}
2057+
2058+
#[cfg(all(feature = "const_generics", not(doc)))]
2059+
#[cfg_attr(docsrs, doc(cfg(feature = "const_generics")))]
19542060
unsafe impl<T, const N: usize> Array for [T; N] {
19552061
type Item = T;
19562062
fn size() -> usize {
19572063
N
19582064
}
19592065
}
19602066

1961-
#[cfg(not(feature = "const_generics"))]
2067+
#[cfg(any(not(feature = "const_generics"), doc))]
19622068
macro_rules! impl_array(
19632069
($($size:expr),+) => {
19642070
$(
@@ -1970,7 +2076,7 @@ macro_rules! impl_array(
19702076
}
19712077
);
19722078

1973-
#[cfg(not(feature = "const_generics"))]
2079+
#[cfg(any(not(feature = "const_generics"), doc))]
19742080
impl_array!(
19752081
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,
19762082
26, 27, 28, 29, 30, 31, 32, 36, 0x40, 0x60, 0x80, 0x100, 0x200, 0x400, 0x600, 0x800, 0x1000,

src/tests.rs

+29
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,35 @@ fn const_generics() {
903903
let _v = SmallVec::<[i32; 987]>::default();
904904
}
905905

906+
#[cfg(feature = "const_new")]
907+
#[test]
908+
fn const_new() {
909+
let v = const_new_inner();
910+
assert_eq!(v.capacity(), 4);
911+
assert_eq!(v.len(), 0);
912+
let v = const_new_inline_sized();
913+
assert_eq!(v.capacity(), 4);
914+
assert_eq!(v.len(), 4);
915+
assert_eq!(v[0], 1);
916+
let v = const_new_inline_args();
917+
assert_eq!(v.capacity(), 2);
918+
assert_eq!(v.len(), 2);
919+
assert_eq!(v[0], 1);
920+
assert_eq!(v[1], 4);
921+
}
922+
#[cfg(feature = "const_new")]
923+
const fn const_new_inner() -> SmallVec<[i32; 4]> {
924+
SmallVec::<[i32; 4]>::new_const()
925+
}
926+
#[cfg(feature = "const_new")]
927+
const fn const_new_inline_sized() -> SmallVec<[i32; 4]> {
928+
crate::smallvec_inline![1; 4]
929+
}
930+
#[cfg(feature = "const_new")]
931+
const fn const_new_inline_args() -> SmallVec<[i32; 2]> {
932+
crate::smallvec_inline![1, 4]
933+
}
934+
906935
#[test]
907936
fn empty_macro() {
908937
let _v: SmallVec<[u8; 1]> = smallvec![];

0 commit comments

Comments
 (0)