generic_upper_bound/
lib.rs

1#![no_std]
2// reason: flagged in macro generated code
3#![allow(clippy::absurd_extreme_comparisons)]
4#![allow(unused_comparisons)]
5
6//! Provides functionality to get a const generic `usize` that is that is a reasonable
7//! upper bound for a specified associated const `usize` for the purpose of intermediate
8//! const calculations, as a workaround for `generic_const_exprs`.
9//!
10//! The API of this crate is structed as follows:
11//! - [`AcceptUpperBound`] is the heart of this crate. Implementors use it to specify which
12//!   generic const they want to be passed and what to do with any given upper bound for it.
13//! - [`eval_with_upper_bound`] is used to get the result of evaluating an upper bound acceptor
14//!   with the best-effort upper bound that this crate can offer.
15//!
16//! While you cannot use this to write a function with a signature that returns e.g. `[T; M + N]`
17//! with generic `M` and `N`, you can use it to temporarily get an array of size `M + N`, use it
18//! to do something useful, then return the result of that computation.
19//! For example, you can concatenate two strings at compile time, even if their value is dependent
20//! on generic paramters:
21//! ```
22//! use generic_upper_bound as gub;
23//! pub trait MyTrait {
24//!     const SOME_STR: &'static str;
25//! }
26//! impl<A, B> MyTrait for (A, B)
27//! where
28//!     A: MyTrait,
29//!     B: MyTrait,
30//! {
31//!     // evaluate our upper bound acceptor to implement concatenation
32//!     const SOME_STR: &'static str = {
33//!         let slice: &'static [u8] = gub::eval_with_upper_bound::<Concat<A, B>>();
34//!
35//!         // take subslice without trailing zeros and convert to string
36//!         let total_length = gub::desired_generic::<Concat<A, B>>();
37//!         match core::str::from_utf8(slice.split_at(total_length).0) {
38//!             Ok(s) => s,
39//!             _ => unreachable!(),
40//!         }
41//!     };
42//! }
43//!
44//! struct Concat<A, B>(A, B);
45//! impl<A: MyTrait, B: MyTrait> gub::AcceptUpperBound for Concat<A, B> {
46//!     type Output = &'static [u8];
47//!     // Want to be passed at least the total length of the strings
48//!     const DESIRED_GENERIC: usize = A::SOME_STR.len() + B::SOME_STR.len();
49//!     // Decide on what to do with each generic const
50//!     type Eval<const UPPER: usize> = ConcatImpl<A, B, UPPER>;
51//! }
52//!
53//! struct ConcatImpl<A, B, const N: usize>(A, B);
54//! impl<A, B, const N: usize> gub::Const for ConcatImpl<A, B, N>
55//! where
56//!     A: MyTrait,
57//!     B: MyTrait,
58//! {
59//!     type Type = &'static [u8];
60//!     // Write the bytes into `[u8; N]` and promote the result
61//!     const VALUE: Self::Type = &{
62//!         let l = A::SOME_STR.as_bytes();
63//!         let r = B::SOME_STR.as_bytes();
64//!         let mut out = [0; N];
65//!         let mut off = 0;
66//!         let mut i = 0; // in >=1.86, you can use split_at_mut and copy_from_slice
67//!         while i < l.len() {
68//!             out[off] = l[i];
69//!             off += 1;
70//!             i += 1;
71//!         }
72//!         i = 0;
73//!         while i < r.len() {
74//!             out[off] = r[i];
75//!             off += 1;
76//!             i += 1;
77//!         }
78//!         out
79//!     };
80//! }
81//!
82//! impl MyTrait for () {
83//!     const SOME_STR: &'static str = "ABC";
84//! }
85//! impl MyTrait for i32 {
86//!     const SOME_STR: &'static str = "123";
87//! }
88//! let concatenated: &'static str = <((), i32)>::SOME_STR;
89//! assert_eq!(concatenated, "ABC123");
90//! ```
91//! Note that this example can be generalized and optimized. For instance, it is possible to accept
92//! any `&'a [&'b str]` where `'b: 'a` as input and this will also be more efficient (most of the
93//! time) due to the overhead from the inexact upper bound used for each concatenation (which will
94//! likely affect the final binary size).
95
96/// A trait for a type that holds a value.
97pub trait Const {
98    /// The type of the const
99    type Type;
100    /// The implementation of the value of the const. Use [`const_value`] for accessing the value.
101    const VALUE: Self::Type;
102}
103
104/// Alias for [`Const::Type`].
105pub type TypeOf<C> = <C as Const>::Type;
106
107/// Alias for [`Const::VALUE`]. Prefer this over accessing `VALUE` directly.
108///
109/// Using the associated constant through this function rather than directly causes it to only be
110/// evaluated when the branch that it is used in is actually executed, assuming that the execution
111/// happens at compile time (i.e. this does not apply to usage in regular `fn`s).
112/// This means that it may improve compile times, avoid errors for recursive consts and avoid evaluating
113/// panics.
114///
115/// For example:
116/// ```
117/// # use generic_upper_bound::*;
118/// struct Fallible;
119/// impl Const for Fallible {
120///     type Type = ();
121///     const VALUE: Self::Type = panic!();
122/// }
123/// const _: () = if false { const_value::<Fallible>() };  // this compiles
124/// ```
125/// ```compile_fail
126/// # use generic_upper_bound::*;
127/// # struct Fallible;
128/// # impl Const for Fallible {
129/// #     type Type = ();
130/// #     const VALUE: Self::Type = panic!();
131/// # }
132/// const _: () = if false { Fallible::VALUE }; // this gives a compile error
133/// ```
134pub const fn const_value<C: Const + ?Sized>() -> C::Type {
135    C::VALUE
136}
137
138/// Allows performing an evaluation of a [`Const`] after converting an associated const `usize` to a
139/// best-effort upper bound `const ...: usize`.
140pub trait AcceptUpperBound {
141    /// The output type of the evaluation.
142    type Output;
143
144    /// The desired value and lower bound that the implementor wants to be passed to [`Self::Eval`].
145    const DESIRED_GENERIC: usize;
146
147    /// Evals the constant by mapping a generic parameter that is at least the desired value
148    /// to the output value. `const_value::<Eval<N>>()` should be indistinguishable for
149    /// all `UPPER_BOUND >= DESIRED` passed to this.
150    type Eval<const UPPER: usize>: Const<Type = Self::Output>;
151}
152
153struct Impl<F>(F);
154
155mod implementation;
156
157/// Returns [`F::DESIRED_GENERIC`](AcceptUpperBound::DESIRED_GENERIC).
158pub const fn desired_generic<F: AcceptUpperBound>() -> usize {
159    Impl::<F>::DESIRED
160}
161
162/// Returns the parameter that [`eval_with_upper_bound`] passes to [`F::Eval`](AcceptUpperBound::Eval).
163pub const fn get_upper_bound<F: AcceptUpperBound>() -> usize {
164    Impl::<F>::ACTUAL
165}
166
167/// Evaluates [`AcceptUpperBound`].
168///
169/// In the language of `generic_const_exprs`, this function returns
170/// `const_value::<F::Eval<{ get_upper_bound::<F>() }>>()`
171pub const fn eval_with_upper_bound<F: AcceptUpperBound>() -> F::Output {
172    Impl::<F>::EVAL
173}