slotmap/lib.rs
1#![doc(html_root_url = "https://docs.rs/slotmap/1.1.1")]
2#![crate_name = "slotmap"]
3#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
4#![cfg_attr(all(nightly, doc), feature(doc_cfg))]
5#![warn(
6 missing_debug_implementations,
7 trivial_casts,
8 trivial_numeric_casts,
9 unused_lifetimes,
10 unused_import_braces
11)]
12#![deny(missing_docs)]
13#![deny(clippy::all)]
14#![allow(
15 clippy::while_let_on_iterator, // Style differences.
16 clippy::unnecessary_map_or // Too high MSRV.
17)]
18
19//! # slotmap
20//!
21//! This library provides a container with persistent unique keys to access
22//! stored values, [`SlotMap`]. Upon insertion a key is returned that can be
23//! used to later access or remove the values. Insertion, removal and access all
24//! take O(1) time with low overhead. Great for storing collections of objects
25//! that need stable, safe references but have no clear ownership otherwise,
26//! such as game entities or graph nodes.
27//!
28//! The difference between a [`BTreeMap`] or [`HashMap`] and a slot map is
29//! that the slot map generates and returns the key when inserting a value. A
30//! key is always unique and will only refer to the value that was inserted.
31//! A slot map's main purpose is to simply own things in a safe and efficient
32//! manner.
33//!
34//! You can also create (multiple) secondary maps that can map the keys returned
35//! by [`SlotMap`] to other values, to associate arbitrary data with objects
36//! stored in slot maps, without hashing required - it's direct indexing under
37//! the hood.
38//!
39//! The minimum required stable Rust version for this crate is 1.58.
40//!
41//! # Examples
42//!
43//! ```
44//! # use slotmap::*;
45//! let mut sm = SlotMap::new();
46//! let foo = sm.insert("foo"); // Key generated on insert.
47//! let bar = sm.insert("bar");
48//! assert_eq!(sm[foo], "foo");
49//! assert_eq!(sm[bar], "bar");
50//!
51//! sm.remove(bar);
52//! let reuse = sm.insert("reuse"); // Space from bar reused.
53//! assert_eq!(sm.contains_key(bar), false); // After deletion a key stays invalid.
54//!
55//! let mut sec = SecondaryMap::new();
56//! sec.insert(foo, "noun"); // We provide the key for secondary maps.
57//! sec.insert(reuse, "verb");
58//!
59//! for (key, val) in sm {
60//! println!("{} is a {}", val, sec[key]);
61//! }
62//! ```
63//!
64//! # Serialization through [`serde`], [`no_std`] support and unstable features
65//!
66//! Both keys and the slot maps have full (de)seralization support through
67//! the [`serde`] library. A key remains valid for a slot map even after one or
68//! both have been serialized and deserialized! This makes storing or
69//! transferring complicated referential structures and graphs a breeze. Care has
70//! been taken such that deserializing keys and slot maps from untrusted sources
71//! is safe. If you wish to use these features you must enable the `serde`
72//! feature flag for `slotmap` in your `Cargo.toml`.
73//!
74//! ```text
75//! slotmap = { version = "1.0", features = ["serde"] }
76//! ```
77//!
78//! This crate also supports [`no_std`] environments, but does require the
79//! [`alloc`] crate to be available. To enable this you have to disable the
80//! `std` feature that is enabled by default:
81//!
82//! ```text
83//! slotmap = { version = "1.0", default-features = false }
84//! ```
85//!
86//! Unfortunately [`SparseSecondaryMap`] is not available in [`no_std`], because
87//! it relies on [`HashMap`]. Finally the `unstable` feature can be defined to
88//! enable the parts of `slotmap` that only work on nightly Rust.
89//!
90//! # Why not index a [`Vec`], or use [`slab`], [`stable-vec`], etc?
91//!
92//! Those solutions either can not reclaim memory from deleted elements or
93//! suffer from the ABA problem. The keys returned by `slotmap` are versioned.
94//! This means that once a key is removed, it stays removed, even if the
95//! physical storage inside the slotmap is reused for new elements. The key is a
96//! permanently unique<sup>*</sup> reference to the inserted value. Despite
97//! supporting versioning, a [`SlotMap`] is often not (much) slower than the
98//! alternative, by internally using carefully checked unsafe code. Finally,
99//! `slotmap` simply has a lot of features that make your life easy.
100//!
101//! # Performance characteristics and implementation details
102//!
103//! Insertion, access and deletion is all O(1) with low overhead by storing the
104//! elements inside a [`Vec`]. Unlike references or indices into a vector,
105//! unless you remove a key it is never invalidated. Behind the scenes each
106//! slot in the vector is a `(value, version)` tuple. After insertion the
107//! returned key also contains a version. Only when the stored version and
108//! version in a key match is a key valid. This allows us to reuse space in the
109//! vector after deletion without letting removed keys point to spurious new
110//! elements. <sup>*</sup>After 2<sup>31</sup> deletions and insertions to the
111//! same underlying slot the version wraps around and such a spurious reference
112//! could potentially occur. It is incredibly unlikely however, and in all
113//! circumstances is the behavior safe. A slot map can hold up to
114//! 2<sup>32</sup> - 2 elements at a time.
115//!
116//! The memory usage for each slot in [`SlotMap`] is `4 + max(sizeof(T), 4)`
117//! rounded up to the alignment of `T`. Similarly it is `4 + max(sizeof(T), 12)`
118//! for [`HopSlotMap`]. [`DenseSlotMap`] has an overhead of 8 bytes per element
119//! and 8 bytes per slot.
120//!
121//! # Choosing [`SlotMap`], [`HopSlotMap`] or [`DenseSlotMap`]
122//!
123//! A [`SlotMap`] is the fastest for most operations, except iteration. It can
124//! never shrink the size of its underlying storage, because it must remember
125//! for each storage slot what the latest stored version was, even if the slot
126//! is empty now. This means that iteration can be slow as it must iterate over
127//! potentially a lot of empty slots.
128//!
129//! [`HopSlotMap`] solves this by maintaining more information on
130//! insertion/removal, allowing it to iterate only over filled slots by 'hopping
131//! over' contiguous blocks of vacant slots. This can give it significantly
132//! better iteration speed. If you expect to iterate over all elements in a
133//! [`SlotMap`] a lot, and potentially have a lot of deleted elements, choose
134//! [`HopSlotMap`]. The downside is that insertion and removal is roughly twice
135//! as slow. Random access is the same speed for both.
136//!
137//! [`DenseSlotMap`] goes even further and stores all elements on a contiguous
138//! block of memory. It uses two indirections per random access; the slots
139//! contain indices used to access the contiguous memory. This means random
140//! access is slower than both [`SlotMap`] and [`HopSlotMap`], but iteration is
141//! significantly faster, as fast as a normal [`Vec`].
142//!
143//! # Choosing [`SecondaryMap`] or [`SparseSecondaryMap`]
144//!
145//! You want to associate extra data with objects stored in a slot map, so you
146//! use (multiple) secondary maps to map keys to that data.
147//!
148//! A [`SecondaryMap`] is simply a [`Vec`] of slots like slot map is, and
149//! essentially provides all the same guarantees as [`SlotMap`] does for its
150//! operations (with the exception that you provide the keys as produced by the
151//! primary slot map). This does mean that even if you associate data to only
152//! a single element from the primary slot map, you could need and have to
153//! initialize as much memory as the original.
154//!
155//! A [`SparseSecondaryMap`] is like a [`HashMap`] from keys to objects, however
156//! it automatically removes outdated keys for slots that had their space
157//! reused. You should use this variant if you expect to store some associated
158//! data for only a small portion of the primary slot map.
159//!
160//! # Custom key types
161//!
162//! If you have multiple slot maps it's an error to use the key of one slot map
163//! on another slot map. The result is safe, but unspecified, and can not be
164//! detected at runtime, so it can lead to a hard to find bug.
165//!
166//! To prevent this, slot maps allow you to specify what the type is of the key
167//! they return. You can construct new key types using the [`new_key_type!`]
168//! macro. The resulting type behaves exactly like [`DefaultKey`], but is a
169//! distinct type. So instead of simply using `SlotMap<DefaultKey, Player>` you
170//! would use:
171//!
172//! ```
173//! # use slotmap::*;
174//! # #[derive(Copy, Clone)]
175//! # struct Player;
176//! new_key_type! { struct PlayerKey; }
177//! let sm: SlotMap<PlayerKey, Player> = SlotMap::with_key();
178//! ```
179//!
180//! You can write code generic over any key type using the [`Key`] trait.
181//!
182//! [`Vec`]: std::vec::Vec
183//! [`BTreeMap`]: std::collections::BTreeMap
184//! [`HashMap`]: std::collections::HashMap
185//! [`serde`]: https://github.com/serde-rs/serde
186//! [`slab`]: https://crates.io/crates/slab
187//! [`stable-vec`]: https://crates.io/crates/stable-vec
188//! [`no_std`]: https://doc.rust-lang.org/1.7.0/book/no-stdlib.html
189
190extern crate alloc;
191
192// So our macros can refer to these.
193#[doc(hidden)]
194pub mod __impl {
195 pub use core::convert::From;
196 pub use core::result::Result;
197
198 #[cfg(feature = "serde")]
199 pub use serde::{Deserialize, Deserializer, Serialize, Serializer};
200}
201
202pub mod basic;
203pub mod dense;
204pub mod hop;
205pub mod secondary;
206#[cfg(feature = "std")]
207pub mod sparse_secondary;
208pub(crate) mod util;
209
210use core::fmt::{self, Debug, Formatter};
211use core::hash::{Hash, Hasher};
212use core::num::NonZeroU32;
213
214#[doc(inline)]
215pub use crate::basic::SlotMap;
216#[doc(inline)]
217pub use crate::dense::DenseSlotMap;
218#[doc(inline)]
219#[allow(deprecated)]
220pub use crate::hop::HopSlotMap;
221#[doc(inline)]
222pub use crate::secondary::SecondaryMap;
223#[cfg(feature = "std")]
224#[doc(inline)]
225pub use crate::sparse_secondary::SparseSecondaryMap;
226
227// Keep Slottable for backwards compatibility, but warn about deprecation
228// and hide from documentation.
229#[doc(hidden)]
230#[deprecated(
231 since = "1.0.0",
232 note = "Slottable is not necessary anymore, slotmap now supports all types on stable."
233)]
234pub trait Slottable {}
235
236#[doc(hidden)]
237#[allow(deprecated)]
238impl<T> Slottable for T {}
239
240/// The actual data stored in a [`Key`].
241///
242/// This implements [`Ord`] so keys can be stored in e.g.
243/// [`BTreeMap`](std::collections::BTreeMap), but the order of keys is
244/// unspecified.
245#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
246pub struct KeyData {
247 idx: u32,
248 version: NonZeroU32,
249}
250
251impl KeyData {
252 const fn new(idx: u32, version: u32) -> Self {
253 debug_assert!(version > 0);
254
255 Self {
256 idx,
257 version: unsafe { NonZeroU32::new_unchecked(version | 1) },
258 }
259 }
260
261 fn null() -> Self {
262 Self::new(u32::MAX, 1)
263 }
264
265 fn is_null(self) -> bool {
266 self.idx == u32::MAX
267 }
268
269 /// Returns the key data as a 64-bit integer. No guarantees about its value
270 /// are made other than that passing it to [`from_ffi`](Self::from_ffi)
271 /// will return a key equal to the original.
272 ///
273 /// With this you can easily pass slot map keys as opaque handles to foreign
274 /// code. After you get them back you can confidently use them in your slot
275 /// map without worrying about unsafe behavior as you would with passing and
276 /// receiving back references or pointers.
277 ///
278 /// This is not a substitute for proper serialization, use [`serde`] for
279 /// that. If you are not doing FFI, you almost surely do not need this
280 /// function.
281 ///
282 /// [`serde`]: crate#serialization-through-serde-no_std-support-and-unstable-features
283 pub fn as_ffi(self) -> u64 {
284 (u64::from(self.version.get()) << 32) | u64::from(self.idx)
285 }
286
287 /// Iff `value` is a value received from `k.as_ffi()`, returns a key equal
288 /// to `k`. Otherwise the behavior is safe but unspecified.
289 pub const fn from_ffi(value: u64) -> Self {
290 let idx = value & 0xffff_ffff;
291 let version = (value >> 32) | 1; // Ensure version is odd.
292 Self::new(idx as u32, version as u32)
293 }
294}
295
296impl Debug for KeyData {
297 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
298 if self.is_null() {
299 f.write_str("null")
300 } else {
301 write!(f, "{}v{}", self.idx, self.version.get())
302 }
303 }
304}
305
306impl Default for KeyData {
307 fn default() -> Self {
308 Self::null()
309 }
310}
311
312impl Hash for KeyData {
313 fn hash<H: Hasher>(&self, state: &mut H) {
314 // A derived Hash impl would call write_u32 twice. We call write_u64
315 // once, which is beneficial if the hasher implements write_u64
316 // explicitly.
317 state.write_u64(self.as_ffi())
318 }
319}
320
321/// Key used to access stored values in a slot map.
322///
323/// Do not use a key from one slot map in another. The behavior is safe but
324/// non-sensical (and might panic in case of out-of-bounds).
325///
326/// To prevent this, it is suggested to have a unique key type for each slot
327/// map. You can create new key types using [`new_key_type!`], which makes a
328/// new type identical to [`DefaultKey`], just with a different name.
329///
330/// This trait is intended to be a thin wrapper around [`KeyData`], and all
331/// methods must behave exactly as if we're operating on a [`KeyData`] directly.
332/// The internal unsafe code relies on this, therefore this trait is `unsafe` to
333/// implement. It is strongly suggested to simply use [`new_key_type!`] instead
334/// of implementing this trait yourself.
335///
336/// # Safety
337/// All methods and trait implementations must behave exactly as if we're
338/// operating on a [`KeyData`] directly.
339pub unsafe trait Key:
340 From<KeyData>
341 + Copy
342 + Clone
343 + Default
344 + Eq
345 + PartialEq
346 + Ord
347 + PartialOrd
348 + core::hash::Hash
349 + core::fmt::Debug
350{
351 /// Creates a new key that is always invalid and distinct from any non-null
352 /// key. A null key can only be created through this method (or default
353 /// initialization of keys made with [`new_key_type!`], which calls this
354 /// method).
355 ///
356 /// A null key is always invalid, but an invalid key (that is, a key that
357 /// has been removed from the slot map) does not become a null key. A null
358 /// is safe to use with any safe method of any slot map instance.
359 ///
360 /// # Examples
361 ///
362 /// ```
363 /// # use slotmap::*;
364 /// let mut sm = SlotMap::new();
365 /// let k = sm.insert(42);
366 /// let nk = DefaultKey::null();
367 /// assert!(nk.is_null());
368 /// assert!(k != nk);
369 /// assert_eq!(sm.get(nk), None);
370 /// ```
371 fn null() -> Self {
372 KeyData::null().into()
373 }
374
375 /// Checks if a key is null. There is only a single null key, that is
376 /// `a.is_null() && b.is_null()` implies `a == b`.
377 ///
378 /// # Examples
379 ///
380 /// ```
381 /// # use slotmap::*;
382 /// new_key_type! { struct MyKey; }
383 /// let a = MyKey::null();
384 /// let b = MyKey::default();
385 /// assert_eq!(a, b);
386 /// assert!(a.is_null());
387 /// ```
388 fn is_null(&self) -> bool {
389 self.data().is_null()
390 }
391
392 /// Gets the [`KeyData`] stored in this key.
393 ///
394 /// # Examples
395 ///
396 /// ```
397 /// # use slotmap::*;
398 /// new_key_type! { struct MyKey; }
399 /// let dk = DefaultKey::null();
400 /// let mk = MyKey::null();
401 /// assert_eq!(dk.data(), mk.data());
402 /// ```
403 fn data(&self) -> KeyData;
404}
405
406/// A helper macro to create new key types. If you use a new key type for each
407/// slot map you create you can entirely prevent using the wrong key on the
408/// wrong slot map.
409///
410/// The type constructed by this macro is defined exactly as [`DefaultKey`],
411/// but is a distinct type for the type checker and does not implicitly convert.
412///
413/// # Examples
414///
415/// ```
416/// # use slotmap::*;
417/// new_key_type! {
418/// // A private key type.
419/// struct RocketKey;
420///
421/// // A public key type with a doc comment.
422/// /// Key for the user slot map.
423/// pub struct UserKey;
424/// }
425///
426/// fn main() {
427/// let mut users = SlotMap::with_key();
428/// let mut rockets = SlotMap::with_key();
429/// let bob: UserKey = users.insert("bobby");
430/// let apollo: RocketKey = rockets.insert("apollo");
431/// // Now this is a type error because rockets.get expects an RocketKey:
432/// // rockets.get(bob);
433///
434/// // If for some reason you do end up needing to convert (e.g. storing
435/// // keys of multiple slot maps in the same data structure without
436/// // boxing), you can use KeyData as an intermediate representation. This
437/// // does mean that once again you are responsible for not using the wrong
438/// // key on the wrong slot map.
439/// let keys = vec![bob.data(), apollo.data()];
440/// println!("{} likes rocket {}",
441/// users[keys[0].into()], rockets[keys[1].into()]);
442/// }
443/// ```
444#[macro_export(local_inner_macros)]
445macro_rules! new_key_type {
446 ( $(#[$outer:meta])* $vis:vis struct $name:ident; $($rest:tt)* ) => {
447 $(#[$outer])*
448 #[derive(Copy, Clone, Default,
449 Eq, PartialEq, Ord, PartialOrd,
450 Hash, Debug)]
451 #[repr(transparent)]
452 $vis struct $name($crate::KeyData);
453
454 impl $crate::__impl::From<$crate::KeyData> for $name {
455 fn from(k: $crate::KeyData) -> Self {
456 $name(k)
457 }
458 }
459
460 unsafe impl $crate::Key for $name {
461 fn data(&self) -> $crate::KeyData {
462 self.0
463 }
464 }
465
466 $crate::__serialize_key!($name);
467
468 $crate::new_key_type!($($rest)*);
469 };
470
471 () => {}
472}
473
474#[cfg(feature = "serde")]
475#[doc(hidden)]
476#[macro_export]
477macro_rules! __serialize_key {
478 ( $name:ty ) => {
479 impl $crate::__impl::Serialize for $name {
480 fn serialize<S>(&self, serializer: S) -> $crate::__impl::Result<S::Ok, S::Error>
481 where
482 S: $crate::__impl::Serializer,
483 {
484 $crate::Key::data(self).serialize(serializer)
485 }
486 }
487
488 impl<'de> $crate::__impl::Deserialize<'de> for $name {
489 fn deserialize<D>(deserializer: D) -> $crate::__impl::Result<Self, D::Error>
490 where
491 D: $crate::__impl::Deserializer<'de>,
492 {
493 let key_data: $crate::KeyData =
494 $crate::__impl::Deserialize::deserialize(deserializer)?;
495 Ok(key_data.into())
496 }
497 }
498 };
499}
500
501#[cfg(not(feature = "serde"))]
502#[doc(hidden)]
503#[macro_export]
504macro_rules! __serialize_key {
505 ( $name:ty ) => {};
506}
507
508new_key_type! {
509 /// The default slot map key type.
510 pub struct DefaultKey;
511}
512
513// Serialization with serde.
514#[cfg(feature = "serde")]
515mod serialize {
516 use serde::{Deserialize, Deserializer, Serialize, Serializer};
517
518 use super::*;
519
520 #[derive(Serialize, Deserialize)]
521 pub struct SerKey {
522 idx: u32,
523 version: u32,
524 }
525
526 impl Serialize for KeyData {
527 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
528 where
529 S: Serializer,
530 {
531 let ser_key = SerKey {
532 idx: self.idx,
533 version: self.version.get(),
534 };
535 ser_key.serialize(serializer)
536 }
537 }
538
539 impl<'de> Deserialize<'de> for KeyData {
540 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
541 where
542 D: Deserializer<'de>,
543 {
544 let mut ser_key: SerKey = Deserialize::deserialize(deserializer)?;
545
546 // Ensure a.is_null() && b.is_null() implies a == b.
547 if ser_key.idx == u32::MAX {
548 ser_key.version = 1;
549 }
550
551 ser_key.version |= 1; // Ensure version is odd.
552 Ok(Self::new(ser_key.idx, ser_key.version))
553 }
554 }
555}
556
557#[cfg(test)]
558mod tests {
559 // Intentionally no `use super::*;` because we want to test macro expansion
560 // in the *users* scope, which might not have that.
561 #[test]
562 fn macro_expansion() {
563 #![allow(dead_code)]
564
565 // Clobber namespace with clashing names - should still work.
566 trait Serialize {}
567 trait Deserialize {}
568 trait Serializer {}
569 trait Deserializer {}
570 trait Key {}
571 trait From {}
572 struct Result;
573 struct KeyData;
574
575 new_key_type! {
576 struct A;
577 pub(crate) struct B;
578 pub struct C;
579 }
580 }
581
582 #[test]
583 fn check_is_older_version() {
584 use super::util::is_older_version;
585
586 let is_older = |a, b| is_older_version(a, b);
587 assert!(!is_older(42, 42));
588 assert!(is_older(0, 1));
589 assert!(is_older(0, 1 << 31));
590 assert!(!is_older(0, (1 << 31) + 1));
591 assert!(is_older(u32::MAX, 0));
592 }
593
594 #[test]
595 fn iters_cloneable() {
596 use super::*;
597
598 struct NoClone;
599
600 let mut sm = SlotMap::new();
601 #[allow(deprecated)]
602 let mut hsm = HopSlotMap::new();
603 let mut dsm = DenseSlotMap::new();
604 let mut scm = SecondaryMap::new();
605 let mut sscm = SparseSecondaryMap::new();
606 scm.insert(sm.insert(NoClone), NoClone);
607 sscm.insert(hsm.insert(NoClone), NoClone);
608 dsm.insert(NoClone);
609
610 let _ = sm.keys().clone();
611 let _ = sm.values().clone();
612 let _ = sm.iter().clone();
613 let _ = hsm.keys().clone();
614 let _ = hsm.values().clone();
615 let _ = hsm.iter().clone();
616 let _ = dsm.keys().clone();
617 let _ = dsm.values().clone();
618 let _ = dsm.iter().clone();
619 let _ = scm.keys().clone();
620 let _ = scm.values().clone();
621 let _ = scm.iter().clone();
622 let _ = sscm.keys().clone();
623 let _ = sscm.values().clone();
624 let _ = sscm.iter().clone();
625 }
626
627 #[cfg(feature = "serde")]
628 #[test]
629 fn key_serde() {
630 use super::*;
631
632 // Check round-trip through serde.
633 let mut sm = SlotMap::new();
634 let k = sm.insert(42);
635 let ser = serde_json::to_string(&k).unwrap();
636 let de: DefaultKey = serde_json::from_str(&ser).unwrap();
637 assert_eq!(k, de);
638
639 // Even if a malicious entity sends up even (unoccupied) versions in the
640 // key, we make the version point to the occupied version.
641 let malicious: KeyData = serde_json::from_str(&r#"{"idx":0,"version":4}"#).unwrap();
642 assert_eq!(malicious.version.get(), 5);
643 }
644}