Skip to main content

typed_index_collections/
lib.rs

1//! The `typed-index-collections` crate provides [`TiSlice`] and [`TiVec`]
2//! structs that are typed index versions of the Rust [`slice`] and
3//! [`std::vec::Vec`] types.
4//!
5//! # Introduction
6//!
7//! The extensive use of slices and vectors instead of references
8//! and smart pointers might be useful for optimization,
9//! Data-Oriented Design and when using Struct of Arrays.
10//! But when dealing with a bunch of slices and vectors
11//! it is easy to accidentally use the wrong index,
12//! which is a common source of bugs.
13//!
14//! # About
15//!
16//! This crate provides [`TiSlice<K, V>`][`TiSlice`] and
17//! [`TiVec<K, V>`][`TiVec`] containers that can be indexed only by the
18//! specified index type `K`. These containers are only wrappers around
19//! the slice primitive [`[V]`][`slice`] and the container
20//! [`std::vec::Vec<V>`][`std::vec::Vec`]. Crate containers mirror the stable
21//! API of the matched Rust containers and forward to them as much as possible.
22//!
23//! [`TiSlice`] and [`TiVec`] can be easily converted to matched Rust containers
24//! and back using [`From`], [`Into`], [`AsRef`] and [`AsMut`] traits.
25//! Also, they expose `raw` property with the original data type.
26//! Containers only require the index to implement
27//! [`From<usize>`][`From`] and [`Into<usize>`][`Into`] traits
28//! that can be easily done with [`derive_more`] crate and
29//! `#[derive(From, Into)]`.
30//!
31//! Note that the crate provides type-safe indexing rather than associative
32//! containers.
33//! [`TiSlice<K, V>`][`TiSlice`] and [`TiVec<K, V>`][`TiVec`] do not preserve
34//! the original indices when sliced.
35//! When you create a slice using a range, the resulting
36//! [`TiSlice<K, V>`][`TiSlice`] will have indices starting at `K::from(0)`.
37//!
38//! # Usage
39//!
40//! First, add the following to your `Cargo.toml`:
41//!
42//! ```toml
43//! [dependencies]
44//! typed-index-collections = "3.5.0"
45//! ```
46//!
47//! This crate depends on the standard library by default that is useful
48//! for debugging and for some extra functionality.
49//! To use this crate in a `#![no_std]` context, use `default-features = false`
50//! in your `Cargo.toml` as shown below:
51//!
52//! ```toml
53//! [dependencies.typed-index-collections]
54//! version = "3.5.0"
55//! default-features = false
56//! features = ["alloc"]
57//! ```
58//!
59//! If you want to use [`derive_more`] for
60//! [`From<usize>`][`From`] and [`Into<usize>`][`Into`] implementation
61//! add it to your `Cargo.toml` as shown below:
62//!
63//! ```toml
64//! [dependencies]
65//! derive_more = "0.99"
66//! typed-index-collections = "3.5.0"
67//! ```
68//!
69//! # Examples
70//!
71//! Simple example with [`derive_more`]:
72#![cfg_attr(feature = "alloc", doc = " ```rust")]
73#![cfg_attr(not(feature = "alloc"), doc = " ```rust,compile_fail")]
74//! use typed_index_collections::TiVec;
75//! use derive_more::{From, Into};
76//!
77//! #[derive(From, Into)]
78//! struct FooId(usize);
79//!
80//! let mut ti_vec: TiVec<FooId, usize> = std::vec![10, 11, 13].into();
81//! ti_vec.insert(FooId(2), 12);
82//! assert_eq!(ti_vec[FooId(2)], 12);
83#![doc = " ```"]
84#![doc = ""]
85//! If a wrong index type is used, compilation will fail:
86//! ```compile_fail
87//! use typed_index_collections::TiVec;
88//! use derive_more::{From, Into};
89//!
90//! #[derive(From, Into)]
91//! struct FooId(usize);
92//!
93//! #[derive(From, Into)]
94//! struct BarId(usize);
95//!
96//! let mut ti_vec: TiVec<FooId, usize> = std::vec![10, 11, 13].into();
97//!
98//! ti_vec.insert(BarId(2), 12);
99//! //            ^^^^^^^^ expected struct `FooId`, found struct `BarId`
100//! assert_eq!(ti_vec[BarId(2)], 12);
101//! //         ^^^^^^^^^^^^^^^^ the trait ... is not implemented for `BarId`
102//! ```
103//!
104//! Another more detailed example with [`derive_more`]:
105#![cfg_attr(feature = "alloc", doc = " ```rust")]
106#![cfg_attr(not(feature = "alloc"), doc = " ```rust,compile_fail")]
107//! use typed_index_collections::{TiSlice, TiVec};
108//! use derive_more::{From, Into};
109//!
110//! #[derive(Clone, Copy, Debug, From, Into, Eq, PartialEq)]
111//! struct FooId(usize);
112//!
113//! #[derive(Clone, Copy, Debug, Eq, PartialEq)]
114//! struct Foo {
115//!     value: usize,
116//! }
117//!
118//! let first = Foo { value: 1 };
119//! let second = Foo { value: 2 };
120//!
121//! let slice_ref = &[first, second][..];
122//! let vec = std::vec![first, second];
123//! let boxed_slice = std::vec![first, second].into_boxed_slice();
124//!
125//! let ti_slice_ref: &TiSlice<FooId, Foo> = slice_ref.as_ref();
126//! let ti_vec: TiVec<FooId, Foo> = vec.into();
127//! let ti_boxed_slice: std::boxed::Box<TiSlice<FooId, Foo>> =
128//!     boxed_slice.into();
129//!
130//! assert_eq!(ti_vec[FooId(1)], second);
131//! assert_eq!(ti_vec.raw[1], second);
132//! assert_eq!(ti_vec.last(), Some(&second));
133//! assert_eq!(ti_vec.last_key_value(), Some((FooId(1), &second)));
134//! assert_eq!(ti_vec.iter_enumerated().next(), Some((FooId(0), &first)));
135//!
136//! let _slice_ref: &[Foo] = ti_slice_ref.as_ref();
137//! let _vec: std::vec::Vec<Foo> = ti_vec.into();
138//! let _boxed_slice: std::boxed::Box<[Foo]> = ti_boxed_slice.into();
139#![doc = " ```"]
140#![doc = ""]
141//! # Feature Flags
142//!
143//! - `alloc` (implied by `std`, enabled by default): Enables the Rust `alloc`
144//!   library, enables [`TiVec`] type, [`ti_vec!`] macro, trait implementations
145//!   for [`Box`]`<`[`TiSlice`]`>`, and some [`TiSlice`] methods that require
146//!   memory allocation.
147//! - `std` (enabled by default): Enables `alloc` feature, the Rust `std`
148//!   library, implements [`std::io::Write`] for [`TiVec`] and implements
149//!   [`std::io::Read`] and [`std::io::Write`] for [`TiSlice`],
150//! - `serde`: Implements [`Serialize`] trait for [`TiSlice`] and [`TiVec`]
151//!   containers and [`Deserialize`] trait for [`Box`]`<`[`TiSlice`]`>` and
152//!   [`TiVec`].
153//! - `bincode`: Implements [`Encode`] trait for [`TiSlice`] and [`TiVec`]
154//!   containers and [`Decode`] and [`BorrowDecode`] traits for
155//!   [`Box`]`<`[`TiSlice`]`>` and [`TiVec`].
156//!
157//!   **Note**: This feature uses `bincode` version `2.0.1`.
158//!   Please be aware that the `bincode` crate is currently marked as
159//!   unmaintained ([RUSTSEC-2025-0141]).
160//!   While the advisory specifically mentions version `1.3.3` as complete,
161//!   version `2.0.1` (released March 2025, long before before the incident)
162//!   remains available and unyanked.
163//!
164//! # Similar crates
165//!
166//! - [`typed_index_collection`] provides a `Vec` wrapper with a very limited
167//!   API. Indices are u32 wrappers, they are not customizable and can only
168//!   index a specific type of container.
169//! - [`indexed_vec`] is the closest copy of the `IndexVec` struct from
170//!   `librustc_index`, but API is also different from standard Rust
171//!   [`std::vec::Vec`] and it has no typed index [`slice`] alternative.
172//! - [`index_vec`] have both [`slice`] and [`std::vec::Vec`] wrapper and API
173//!   closer to standard API. But it implicitly allows you to use `usize` for
174//!   get methods and index expressions that reduce type-safety, and the macro
175//!   `define_index_type!` which is used to generate a newtyped index struct,
176//!   implicitly implements a lot of traits that in my opinion would be better
177//!   implemented only when necessary using crates intended for this, such as
178//!   [`derive_more`].
179//!
180//! # License
181//!
182//! Licensed under either of
183//!
184//! - Apache License, Version 2.0 ([LICENSE-APACHE](https://github.com/zheland/typed-index-collections/blob/master/LICENSE-APACHE)
185//!   or <https://www.apache.org/licenses/LICENSE-2.0>)
186//! - MIT license ([LICENSE-MIT](https://github.com/zheland/typed-index-collections/blob/master/LICENSE-MIT)
187//!   or <https://opensource.org/licenses/MIT>)
188//!
189//! at your option.
190//!
191//! ## Contribution
192//!
193//! Unless you explicitly state otherwise, any contribution intentionally
194//! submitted for inclusion in the work by you, as defined in the Apache-2.0
195//! license, shall be dual licensed as above, without any
196//! additional terms or conditions.
197//!
198//! [`TiSlice`]: struct.TiSlice.html
199//! [`TiVec`]: struct.TiVec.html
200//! [`ti_vec!`]: macro.ti_vec.html
201//! [`slice`]: https://doc.rust-lang.org/std/primitive.slice.html
202//! [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html
203//! [`Rc`]: https://doc.rust-lang.org/std/rc/struct.Rc.html
204//! [`Weak`]: https://doc.rust-lang.org/std/rc/struct.Weak.html
205//! [`std::vec::Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
206//! [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
207//! [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
208//! [`From`]: https://doc.rust-lang.org/std/convert/trait.From.html
209//! [`Into`]: https://doc.rust-lang.org/std/convert/trait.Into.html
210//! [`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
211//! [`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
212//! [`derive_more`]: https://crates.io/crates/derive_more
213//! [`typed_index_collection`]: https://crates.io/crates/typed_index_collection
214//! [`indexed_vec`]: https://crates.io/crates/indexed_vec
215//! [`index_vec`]: https://crates.io/crates/index_vec
216//! [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
217//! [`Deserialize`]: https://docs.serde.rs/serde/trait.Deserialize.html
218//! [`Encode`]: https://docs.serde.rs/serde/trait.Serialize.html
219//! [`Decode`]: https://docs.rs/bincode/latest/bincode/de/trait.Decode.html
220//! [`BorrowDecode`]: https://docs.rs/bincode/latest/bincode/de/trait.BorrowDecode.html
221//! [RUSTSEC-2025-0141]: https://rustsec.org/advisories/RUSTSEC-2025-0141
222
223#![cfg_attr(docsrs, feature(doc_cfg))]
224#![no_std]
225
226#[cfg(any(feature = "alloc", test))]
227extern crate alloc;
228
229#[cfg(feature = "std")]
230extern crate std;
231
232#[cfg(test)]
233#[macro_use]
234mod test_util;
235
236mod iter;
237mod range;
238mod slice;
239
240#[cfg(feature = "alloc")]
241#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
242mod macros;
243#[cfg(feature = "alloc")]
244#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
245mod vec;
246
247pub use iter::{TiEnumerated, TiSliceKeys, TiSliceMutMap, TiSliceRefMap};
248pub use range::TiRangeBounds;
249pub use slice::{TiSlice, TiSliceIndex};
250#[cfg(feature = "alloc")]
251#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
252pub use vec::TiVec;
253
254#[cfg(test)]
255mod integration_tests_deps {
256    use {readme_sync as _, serde_json as _, version_sync as _};
257}
258
259#[doc(hidden)]
260pub mod macro_deps {
261    #[cfg(feature = "alloc")]
262    pub use alloc::vec;
263}