stack_collections/
lib.rs

1//! `stack_collections`: Stack-allocated collections for Rust
2//!
3//! Provides [`StackString`], [`StackVec`], and [`StackArrayString`].
4//! These are fixed-capacity, stack-allocated collections designed for
5//! `no_std` environments, zero heap allocations, predictable memory usage,
6//! and deterministic performance.
7#![deny(warnings)]
8#![deny(clippy::all)]
9#![deny(clippy::pedantic)]
10#![deny(clippy::nursery)]
11#![deny(clippy::undocumented_unsafe_blocks)]
12#![deny(clippy::multiple_unsafe_ops_per_block)]
13#![deny(clippy::semicolon_if_nothing_returned)]
14#![deny(clippy::std_instead_of_core)]
15#![deny(clippy::std_instead_of_alloc)]
16#![deny(clippy::missing_inline_in_public_items)]
17#![deny(clippy::return_self_not_must_use)]
18#![no_std]
19#![cfg_attr(feature = "std", allow(unused))]
20#![cfg_attr(feature = "nightly", feature(ascii_char))]
21#![cfg_attr(docsrs, feature(doc_cfg))]
22
23#[cfg(feature = "alloc")]
24extern crate alloc;
25#[cfg(feature = "std")]
26extern crate std;
27
28/// Internal module that are not meant for users to use.
29mod internal {
30    /// Helper macro for creating an empty collection.
31    macro_rules! empty_collection {
32        () => {
33            Self {
34                // SAFETY: An array of MaybeUninit does not require initialization
35                buf: unsafe { MaybeUninit::uninit().assume_init() },
36                len: 0,
37            }
38        };
39    }
40
41    /// Helper macro to define unchecked, normal, and try variants of a method.
42    macro_rules! define_variants {
43    (
44        $(#[$meta:meta])*
45        fn $name:ident($self:ident: $self_ty:ty $(, $param:ident: $param_ty:ty)*) $(-> $ret:ty)?,
46        $(where_clause: { $($where_clause:tt)* } )?
47
48        normal_brief: $normal_brief:literal,
49        try_brief: $try_brief:literal,
50        unchecked_brief_suffix: $unchecked_brief_suffix:literal,
51        ub_conditions: {
52            $($ub_condition:expr => $error:literal),+ $(,)?
53        },
54        prefixes: {
55            normal: {$($normal_prefix:tt)*},
56            unchecked: {$($unchecked_prefix:tt)*},
57            try: {$($try_prefix:tt)*},
58        },
59        unchecked_fn: $unchecked_fn:ident,
60        try_fn: $try_fn:ident,
61        body:  $body:tt,
62        $(examples: {
63            normal: { $($ex_normal:tt)* }
64            try: { $($ex_try:tt)* }
65        })?
66    ) => {
67        $(#[$meta])*
68        #[doc = concat!(" ", $normal_brief, ", ", $unchecked_brief_suffix, ".")]
69        ///
70        #[doc = concat!(" See also [`Self::", stringify!($name), "`] for the safe version and [`Self::", stringify!($try_fn), "`] for the [`Option`] returning version.")]
71        ///
72        /// # Safety
73        ///
74        /// Calling this function when any of the following conditions are **`true`** is **undefined behavior**:
75        $( #[doc = concat!(" - `", stringify!($ub_condition), "`")] )+
76        #[inline]
77        $($unchecked_prefix)* unsafe fn $unchecked_fn($self: $self_ty $(, $param: $param_ty)*) $(-> $ret)?
78        $(where $($where_clause)*)?
79        {
80            $( debug_assert!(!($ub_condition), $error); )+
81            $body
82        }
83
84        $(#[$meta])*
85        #[doc = concat!(" ", $normal_brief, ".")]
86        ///
87        #[doc = concat!(" See also [`Self::", stringify!($unchecked_fn), "`] for the unchecked version and [`Self::", stringify!($try_fn), "`] for the [`Option`] returning version.")]
88        ///
89        /// # Panics
90        ///
91        $( #[doc = concat!(" - \"", $error, "\" if `", stringify!($ub_condition), "`")] )+
92        $(
93            ///
94            /// # Examples
95            ///
96            $($ex_normal)*
97        )?
98        #[inline]
99        $($normal_prefix)* fn $name($self: $self_ty $(, $param: $param_ty)*) $(-> $ret)?
100        $(where $($where_clause)*)?
101        {
102            $( assert!(!($ub_condition), $error); )+
103            // SAFETY: passed all undefined behaviour conditions above
104            unsafe { $self.$unchecked_fn($($param),*) }
105        }
106
107        $(#[$meta])*
108        #[doc = concat!(" ", $try_brief, ".")]
109        ///
110        #[doc = concat!(" See also [`Self::", stringify!($name), "`] for the panic-on-error version and [`Self::", stringify!($unchecked_fn), "`] for the unchecked version.")]
111        ///
112        /// Returns [`None`] if any of these conditions are **`true`**:
113        $( #[doc = concat!(" - `", stringify!($ub_condition), "`")] )+
114        $(
115            ///
116            /// # Examples
117            ///
118            $($ex_try)*
119        )?
120        #[must_use]
121        #[inline]
122        $($try_prefix)* fn $try_fn($self: $self_ty $(, $param: $param_ty)*) $(-> Option<$ret>)?
123        $(where $($where_clause)*)?
124        {
125            $( if $ub_condition { return None; } )+
126            // SAFETY: passed all undefined behaviour conditions above
127            let result = unsafe { $self.$unchecked_fn($($param),*) };
128            Some(result)
129        }
130    };
131}
132
133    pub(crate) use define_variants;
134    pub(crate) use empty_collection;
135}
136
137/// A UTF-8–encoded, stack-allocated, fixed-capacity string.
138pub mod stack_string;
139
140/// A stack-allocated vector with a fixed capacity.
141pub mod stack_vec;
142
143pub use crate::stack_string::StackString;
144pub use crate::stack_vec::StackVec;
145
146/// A stack-allocated array of small strings.
147///
148/// This is a convenience alias for `StackVec<StackString<N>, CAP>`,
149/// useful when you need a fixed-capacity collection of short strings.
150///
151/// # Examples
152///
153/// ```
154/// use stack_collections::StackArrayString;
155///
156/// let mut arr: StackArrayString<16, 4> = StackArrayString::new();
157///
158/// arr.push("hello".try_into().unwrap());
159/// arr.push("world".try_into().unwrap());
160///
161/// assert_eq!(arr.len(), 2);
162/// assert_eq!(arr.capacity(), 4);
163/// assert_eq!(arr[0].capacity(), 16);
164/// assert_eq!(arr[0].as_str(), "hello");
165/// assert_eq!(arr[1].as_str(), "world");
166/// ```
167pub type StackArrayString<const N: usize, const CAP: usize> = StackVec<StackString<N>, CAP>;