Skip to main content

intid_core/
trusted.rs

1//! Since [`IntegerId`] and [`IntegerIdContiguous`] are safe traits,
2//! it is not possible for unsafe code to rely on them for correctness.
3//!
4//! There is an exception for [`IntegerId::from_int_unchecked`],
5//! which is required to accept any value returned from [`IntegerId::to_int`].
6//! If [`IntegerIdContiguous`] is implemented,
7//! the `from_int_unchecked` function must also accept all values in the range `Self::MIN_ID..=Self::MAX_ID`.
8//!
9//! These assumptions allow zero-cost conversions from `T::Int` to `T`,
10//! but do not affect fully-safe implementations of `IntegerId`
11//! where [`IntegerId::from_int_unchecked`] simply delegates to [`IntegerId::from_int`].
12//!
13//! Because it is a fully safe function in a fully safe trait,
14//! there is no way for us to trust its results.
15//!
16//! In order to work around this, we provide special "trust tokens".
17//! These tokens are associated constants on the trait,
18//! so that they are always resolved at compile time.
19//! These require invoking an `unsafe` code to construct the token,
20//! to prevent fully safe code from being able to trigger undefined behavior.
21//!
22//! [`IntegerIdContiguous`]: crate::IntegerIdContiguous
23
24use core::marker::PhantomData;
25
26use crate::IntegerId;
27
28/// Indicates that an [`IntegerId`] unsafely guarantees that the result of [`IntegerId::to_int`]
29/// will always fall in the range `IntegerId::MIN_INT..=IntegerId::MAX_ID`.
30///
31/// Also guarantees that the [`IntegerId::to_int`] is implemented in the expected manner.
32///
33/// Just because the type implements something like [`bytemuck::Contiguous`] does not mean
34/// that it is valid to create the token, as [`IntegerId::to_int`] could still be implemented incorrectly.
35#[derive(Copy, Clone)]
36pub struct TrustedRangeToken<T: IntegerId> {
37    marker: PhantomData<&'static T>,
38}
39impl<T: IntegerId> TrustedRangeToken<T> {
40    /// Promise that the type `T` satisfies the appropriate correctness requirements.
41    ///
42    /// # Safety
43    /// If the [`IntegerId`] does not meet the requirements,
44    /// this is immediate undefined behavior (similar to constructing a `!` type).
45    pub const unsafe fn assume_valid() -> Self {
46        TrustedRangeToken {
47            marker: PhantomData,
48        }
49    }
50
51    /// Promise that the type `T` satisfies the appropriate correctness whenever `U` promises to.
52    ///
53    /// This function is helpful for implementing newtype wrappers around an arbitrary inner type.
54    ///
55    /// This is equivalent to `T::TRUSTED_RANGE::map(|| unsafe { TrustedRangeToken::assume_valid() })`,
56    /// but works in a `const` context.
57    ///
58    /// # Safety
59    /// You must ensure that `U` can be trusted with the requirements of [`TrustedRangeToken`]
60    /// whenever `U` meets those same requirements.
61    pub const unsafe fn assume_valid_if<U: IntegerId>() -> Option<Self> {
62        if <U as IntegerId>::TRUSTED_RANGE.is_some() {
63            // SAFETY: Caller guarantees that T is trusted whenever U is
64            Some(unsafe { TrustedRangeToken::<T>::assume_valid() })
65        } else {
66            None
67        }
68    }
69}