intid_core/lib.rs
1//! Defines the [`IntegerId`] trait, for types that can be identified by an integer value.
2//!
3//! This contains all the same types that the [`intid`] crate does,
4//! but has no dependency on [`intid_derive`] (even when the `intid/derive` feature is enabled).
5//! This reduces compile times, similar to the separation between `serde_core` and `serde` introduced in [serde-rs/serde#2608].
6//!
7//! It may be convenient to rename the `intid_core` dependency to `intid` using [dependency renaming].
8//! ```toml
9//! intid = { version = "0.3", package = "intid_core" }
10//! ```
11//! This renaming comes at no loss of clarity,
12//! since the items in `intid_core` are simply a subset of the items in the `intid` crate.
13//! If for some reason you decide to use `intid_derive` directly without depending on `intid`,
14//! then you will need to do this renaming since the derived code references the `intid` crate.
15//!
16//! [dependency renaming]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml
17//! [serde-rs/serde#2608]: https://github.com/serde-rs/serde/pull/2608
18//! [`intid`]: https://docs.rs/intid/latest/intid
19//! [`intid_derive`]: https://docs.rs/intid-derive/latest/intid_derive
20#![no_std]
21#![cfg_attr(feature = "nightly", feature(never_type,))]
22
23use core::fmt::Debug;
24
25#[macro_use]
26mod macros;
27mod impls;
28pub mod trusted;
29pub mod uint;
30pub mod utils;
31
32pub use uint::UnsignedPrimInt;
33
34/// An identifier which can be sensibly converted to/from an unsigned integer value.
35///
36///
37/// The type should not carry any information beyond that of the integer index,
38/// and be able to losslessly convert back and forth from [`Self::Int`].
39/// It is possible that not all values of the underlying integer type are valid,
40/// allowing [`core::num::NonZero`] and C-like enums to implement this trait.
41///
42///
43/// This is intended mostly for newtype wrappers around integer indexes,
44/// and the primitive integer types themselves.
45///
46/// The value of the underlying integer must be consistent.
47/// It cannot change over the course of the program's lifetime.
48///
49/// ## Safety
50/// With one exception, this trait is safe to implement and cannot be relied upon by memory safety.
51///
52/// If the implementation of [`IntegerId::from_int_unchecked`] makes any sort of unsafe assumptions
53/// about the validity of the input, then the rest of the trait must be implemented correctly.
54/// This means that implementations of this trait fall into two categories:
55/// 1. Potentially incorrect implemented entirely using safe code, where `from_int_unchecked(x)`
56/// is equivalent to calling `from_int_checked(x).unwrap()`;
57/// 2. Traits where `from_int_unchecked` could trigger undefined behavior on an invalid value,
58/// but every other part of this trait can be trusted to be implemented correctly.
59///
60/// In both these cases, the following code is always safe:
61/// ```no_run
62/// # use intid_core::IntegerId;
63/// fn foo<T: IntegerId>(x: T) -> T {
64/// let y = x.to_int();
65/// let z = unsafe { T::from_int_unchecked(y) };
66/// z
67/// }
68/// ```
69/// In case 1, it doesn't matter if [`x.to_int()`](Self::to_int) produces garbage data,
70/// because `T::from_int_unchecked` method is safe to call.
71/// In case 2, the `to_int` method can be trusted to produce a valid value `y` that cannot fail
72/// when passed to `T::from_int_unchecked`.
73///
74/// The requirement for correctness in this case also apply to all sub-traits in this crate,
75/// including [`IntegerIdContiguous`] and [`IntegerIdCounter`].
76/// So an unsafe implementation of `from_int_unchecked` can be similarly trusted to accept
77/// all integer values between [`IntegerId::MIN_ID`] and [`IntegerId::MAX_ID`].
78///
79/// This restriction allows avoiding unnecessary checks when ids are stored to/from another data structure.
80/// Despite this requirement, I still consider this trait safe to implement,
81/// because safety can only be violated by an unsafe implementation of`from_int_unchecked`.
82///
83/// This type should not have interior mutability.
84/// This is guaranteed by the `Copy` bound.
85pub trait IntegerId: Copy + Eq + Debug + Send + Sync + 'static {
86 /// The underlying integer type.
87 ///
88 /// Every valid instance of `Self` should correspond to a valid `Self::Int`.
89 /// However, the other direction may not always be true.
90 type Int: uint::UnsignedPrimInt;
91 /// The value of this type with the smallest integer value,
92 /// or `None` if this type is uninhabited.
93 const MIN_ID: Option<Self>;
94 /// The value of this type with the largest integer value,
95 /// or `None` if this type is uninhabited.
96 const MAX_ID: Option<Self>;
97 /// The value of [`Self::MIN_ID`] a primitive integer,
98 /// or `None` if this type is uninhabited.
99 ///
100 /// This is necessary because trait methods cannot be marked `const`.
101 const MIN_ID_INT: Option<Self::Int>;
102 /// The value of [`Self::MAX_ID`] a primitive integer,
103 /// or `None` if this type is uninhabited.
104 ///
105 /// This is necessary because trait methods cannot be marked `const`.
106 const MAX_ID_INT: Option<Self::Int>;
107
108 /// Indicates that the type's implementation of [`IntegerId::to_int`] can trusted
109 /// to only return values in the range `MIN_ID_INT..=MAX_ID_INT`.
110 ///
111 /// This can be relied upon by unsafe code, since the token is `unsafe` to construct.
112 const TRUSTED_RANGE: Option<trusted::TrustedRangeToken<Self>> = None;
113
114 /// Create an id from the underlying integer value,
115 /// panicking if the value is invalid.
116 ///
117 /// ## Correctness
118 /// A value returned by this method should never trigger
119 /// an error if passed to [`Self::from_int_checked`].
120 /// This means the validity of certain ids can't change over the course of the program.
121 #[inline]
122 #[track_caller]
123 fn from_int(id: Self::Int) -> Self {
124 match Self::from_int_checked(id) {
125 Some(success) => success,
126 None => uint::invalid_id(id),
127 }
128 }
129
130 /// Create an id from the underlying integer value,
131 /// returning `None` if the value is invalid.
132 fn from_int_checked(id: Self::Int) -> Option<Self>;
133
134 /// Create an id from the underlying integer value,
135 /// triggering undefined behavior if the value is invalid.
136 ///
137 /// ## Safety
138 /// If the corresponding [`Self::from_int_checked`] method would fail,
139 /// this triggers undefined behavior.
140 /// The default implementation just invokes [`Self::from_int`].
141 #[inline]
142 unsafe fn from_int_unchecked(id: Self::Int) -> Self {
143 Self::from_int(id)
144 }
145
146 /// Convert this id into an underlying integer type.
147 ///
148 /// This method can never fail,
149 /// since valid instances `Self` always correspond to valid instances of `Self::Int`.
150 fn to_int(self) -> Self::Int;
151}
152
153/// Indicates that an id occupies contiguous range of contiguous values,
154/// and all values between [`IntegerId::MIN_ID`] and [`IntegerId::MAX_ID`] are valid.
155///
156/// This is similar to [`bytemuck::Contiguous`].
157/// However, since it is safe to implement,
158/// it must not be relied upon for correctness.
159///
160/// ## Safety
161/// This trait is safe to implement, so may not usually be relied upon for memory safety.
162///
163/// However, if [`Self::from_int_unchecked`](IntegerId::from_int_unchecked) makes unsafe assumptions (satisfying the condition set forth in the [`IntegerId`] safety docs),
164/// then this trait must also be implemented correctly.
165/// More specifically, all integers between [`IntegerId::MIN_ID`] and [`IntegerId::MAX_ID`] must be valid
166/// and cannot fail when passed to [`IntegerId::from_int_checked`].
167pub trait IntegerIdContiguous: IntegerId {}
168
169/// An [`IntegerId`] that can be sensibly used as a counter,
170/// starting at a [`Self::START`] value and being incremented from there.
171///
172/// This is used by the `intid-allocator` crate to provide an atomic counter to allocate new ids.
173/// It also provides more complex allocators that can reuse ids that have been freed.
174///
175/// This type cannot be implemented for uninhabited types like [`core::convert::Infallible`] or `!`,
176/// as there is no valid implementation of [`Self::START`].
177pub trait IntegerIdCounter: IntegerId + IntegerIdContiguous {
178 /// Where a counter a should start from.
179 ///
180 /// This should be the [`Default`] value if one is defined.
181 /// It is usually equal to the [`IntegerId::MIN_ID`],
182 /// but this is not required.
183 const START: Self;
184 /// The value of [`Self::START`] as a [`T::Int`](IntegerId::Int).
185 ///
186 /// This is necessary because trait methods ([`IntegerId::to_int`])
187 /// can not currently be const methods.
188 const START_INT: Self::Int;
189
190 /// Increment this value by the specified offset,
191 /// returning `None` if the value overflows or is invalid.
192 ///
193 /// This should behave consistently with [`IntegerIdContiguous`]
194 /// and [`IntegerId::from_int_checked`].
195 /// However, that can not be relied upon for memory safety.
196 ///
197 /// This is implemented as an associated method to avoid namespace pollution.
198 #[inline]
199 fn checked_add(this: Self, offset: Self::Int) -> Option<Self> {
200 uint::checked_add(this.to_int(), offset).and_then(Self::from_int_checked)
201 }
202
203 /// Increment this value by the specified offset,
204 /// returning `None` if the value overflows or is invalid.
205 ///
206 /// This should behave consistently with [`IntegerIdContiguous`]
207 /// and [`IntegerId::from_int_checked`].
208 /// However, that can not be relied upon for memory safety.
209 ///
210 /// This is implemented as an associated method to avoid namespace pollution.
211 #[inline]
212 fn checked_sub(this: Self, offset: Self::Int) -> Option<Self> {
213 uint::checked_sub(this.to_int(), offset).and_then(Self::from_int_checked)
214 }
215}
216
217/// A type that can be for lookup as an [`IntegerId`].
218///
219/// Used for key lookup in maps, similar to [`core::borrow::Borrow`] or [`equivalent::Equivalent`].
220/// These traits are not suitable for id maps,
221/// which need conversion to integers rather than hashing/equality.
222///
223/// [`equivalent::Equivalent`]: https://docs.rs/equivalent/latest/equivalent/trait.Equivalent.html
224pub trait EquivalentId<K: IntegerId> {
225 /// Convert this type to an id `K`.
226 fn as_id(&self) -> K;
227}
228impl<K: IntegerId> EquivalentId<K> for K {
229 #[inline]
230 fn as_id(&self) -> K {
231 *self
232 }
233}
234impl<K: IntegerId> EquivalentId<K> for &'_ K {
235 #[inline]
236 fn as_id(&self) -> K {
237 **self
238 }
239}
240impl<K: IntegerId> EquivalentId<K> for &'_ mut K {
241 #[inline]
242 fn as_id(&self) -> K {
243 **self
244 }
245}