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//! This crate allows performing const calculations with the help of a generic const `usize`
7//! that is a reasonable upper bound of some desired associated const `usize`.
8//!
9//! The API of this crate is structed as follows:
10//! - [`AcceptUpperBound`] is the heart of this crate. Implementors use it to specify which
11//! generic const they want to be passed to them and what to do with any given upper bound for it.
12//! It can be implemented conveniently using [`impl_accept_upper_bound!`].
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 parameters:
21//! ```
22//! use generic_upper_bound as gub;
23//! pub trait MyTrait {
24//! const SOME_STR: &'static str;
25//! }
26//! struct Concat<A, B>(A, B);
27//! gub::impl_accept_upper_bound! {
28//! impl{A: MyTrait, B: MyTrait} Concat<A, B>;
29//!
30//! const DESIRED_GENERIC: usize = A::SOME_STR.len() + B::SOME_STR.len();
31//!
32//! const EVAL<const UPPER: usize>: &'static [u8] = &{
33//! let l = A::SOME_STR.as_bytes();
34//! let r = B::SOME_STR.as_bytes();
35//! let mut out = [0; UPPER];
36//! let mut off = 0;
37//! // after 1.86, use split_at_mut and copy_from_slice
38//! let mut i = 0;
39//! while i < l.len() {
40//! out[off] = l[i];
41//! off += 1;
42//! i += 1;
43//! }
44//! i = 0;
45//! while i < r.len() {
46//! out[off] = r[i];
47//! off += 1;
48//! i += 1;
49//! }
50//! out
51//! };
52//! }
53//! impl<A: MyTrait, B: MyTrait> MyTrait for (A, B) {
54//! // evaluate the upper bound acceptor, trim trailing nul bytes
55//! // and convert to string
56//! const SOME_STR: &'static str = match core::str::from_utf8(
57//! gub::eval_with_upper_bound::<Concat<A, B>>()
58//! .split_at(gub::desired_generic::<Concat<A, B>>())
59//! .0,
60//! ) {
61//! Ok(s) => s,
62//! _ => unreachable!(),
63//! };
64//! }
65//! impl MyTrait for () {
66//! const SOME_STR: &'static str = "ABC";
67//! }
68//! impl MyTrait for i32 {
69//! const SOME_STR: &'static str = "123";
70//! }
71//! let concatenated: &'static str = <((), i32)>::SOME_STR;
72//! assert_eq!(concatenated, "ABC123");
73//! ```
74//! Note that this example can be generalized and optimized. For instance, it is possible to accept
75//! any `&'a [&'b str]` as input and this will also be more efficient (most of the time)
76//! due to the overhead from the inexact upper bound used for each concatenation (which will
77//! likely affect the final binary size).
78//!
79//! # MSRV
80//! The MSRV is 1.78. This is to allow this crate to be used as a workaround for the breaking change
81//! to const promotion that was introduced by that version.
82
83/// A trait for a type that holds a value.
84pub trait Const {
85 /// The type of the const
86 type Type;
87 /// The implementation of the value of the const. Use [`const_value`] for accessing the value.
88 const VALUE: Self::Type;
89}
90
91/// Alias for [`Const::Type`].
92pub type TypeOf<C> = <C as Const>::Type;
93
94/// Alias for [`Const::VALUE`]. Prefer this over accessing `VALUE` directly.
95///
96/// Using the associated constant through this function rather than directly causes it to only be
97/// evaluated when the branch that it is used in is actually executed, assuming that the execution
98/// happens at compile time (i.e. this does not apply to usage in regular `fn`s).
99/// This means that it may improve compile times, avoid errors for recursive consts and avoid evaluating
100/// panics.
101///
102/// For example:
103/// ```
104/// # use generic_upper_bound::*;
105/// struct Fallible;
106/// impl Const for Fallible {
107/// type Type = ();
108/// const VALUE: Self::Type = panic!();
109/// }
110/// const _: () = if false { const_value::<Fallible>() }; // this compiles
111/// ```
112/// ```compile_fail
113/// # use generic_upper_bound::*;
114/// # struct Fallible;
115/// # impl Const for Fallible {
116/// # type Type = ();
117/// # const VALUE: Self::Type = panic!();
118/// # }
119/// const _: () = if false { Fallible::VALUE }; // this gives a compile error
120/// ```
121pub const fn const_value<C: Const + ?Sized>() -> C::Type {
122 C::VALUE
123}
124
125/// Allows implementing a callback pattern that accepts an upper bound for a desired generic const
126/// parameter.
127///
128/// The trait works by allowing implementors to specify, for each upper bound, a value that is
129/// computed from that upper bound. This value is represented as a generic associated type with
130/// bound [`Const`], as this is currently the only way to implement a `const` item with generics
131/// in a trait.
132///
133/// When passed to [`eval_with_upper_bound`], [`Eval::<UPPER>::VALUE`](Const::VALUE) will be evaluated
134/// with a parameter `UPPER` that satisfies `DESIRED_GENERIC <= UPPER < 2 * DESIRED_GENERIC`.
135pub trait AcceptUpperBound {
136 /// The output type of the evaluation.
137 type Output;
138
139 /// The desired value and lower bound that the implementor wants to be passed to [`Self::Eval`].
140 const DESIRED_GENERIC: usize;
141
142 /// Evals the constant by mapping a generic parameter that is at least the desired value
143 /// to the output value. `const_value::<Eval<UPPER>>()` should be indistinguishable for
144 /// all *possible* generic parameters passed to this.
145 type Eval<const UPPER: usize>: Const<Type = Self::Output>;
146}
147
148struct Impl<F>(F);
149
150mod implementation;
151
152/// Returns [`F::DESIRED_GENERIC`](AcceptUpperBound::DESIRED_GENERIC).
153pub const fn desired_generic<F: AcceptUpperBound>() -> usize {
154 Impl::<F>::DESIRED
155}
156
157/// Returns the parameter that [`eval_with_upper_bound`] passes to [`F::Eval`](AcceptUpperBound::Eval).
158pub const fn get_upper_bound<F: AcceptUpperBound>() -> usize {
159 Impl::<F>::ACTUAL
160}
161
162/// Evaluates [`AcceptUpperBound`].
163///
164/// In the language of `generic_const_exprs`, this function returns
165/// `const_value::<F::Eval<{ get_upper_bound::<F>() }>>()`
166pub const fn eval_with_upper_bound<F: AcceptUpperBound>() -> F::Output {
167 Impl::<F>::EVAL
168}
169
170/// Implements [`AcceptUpperBound`] by generating a hidden [`Const`] implementor.
171///
172/// Generic parameters are passed in braces (`{...}`) after `impl` and cannot have a trailing
173/// comma. Where bounds are optionally passed in braces after the implementing type.
174///
175/// The example from the [crate level documentation](crate) can be written manually like this:
176/// ```
177/// use generic_upper_bound as gub;
178/// pub trait MyTrait {
179/// const SOME_STR: &'static str;
180/// }
181/// impl<A: MyTrait, B: MyTrait> MyTrait for (A, B) {
182/// const SOME_STR: &'static str = match core::str::from_utf8(
183/// gub::eval_with_upper_bound::<Concat<A, B>>()
184/// .split_at(gub::desired_generic::<Concat<A, B>>())
185/// .0,
186/// ) {
187/// Ok(s) => s,
188/// _ => unreachable!(),
189/// };
190/// }
191///
192/// struct Concat<A, B>(A, B);
193/// impl<A: MyTrait, B: MyTrait> gub::AcceptUpperBound for Concat<A, B> {
194/// type Output = &'static [u8];
195/// // Want to be passed at least the total length of the strings
196/// const DESIRED_GENERIC: usize = A::SOME_STR.len() + B::SOME_STR.len();
197/// // Decide on what to do with each generic const
198/// type Eval<const UPPER: usize> = ConcatImpl<A, B, UPPER>;
199/// }
200/// struct ConcatImpl<A, B, const N: usize>(A, B);
201/// impl<A: MyTrait, B: MyTrait, const N: usize> gub::Const for ConcatImpl<A, B, N> {
202/// type Type = &'static [u8];
203/// const VALUE: Self::Type = panic!("...");
204/// }
205/// ```
206#[macro_export]
207macro_rules! impl_accept_upper_bound {
208 {
209 $(#[$meta:meta])*
210 impl{$($params:tt)*} $Self:ty $({ $($where_bounds:tt)* })?;
211
212 const DESIRED_GENERIC: $usize_d:ty = $DESIRED_GENERIC:expr;
213 const EVAL<const $UPPER:ident: $usize_e:ty>: $Output:ty = $EVAL:expr;
214
215 } => {
216 const _: () = {
217 pub struct __Eval<__Eval, const $UPPER: $usize_e>(__Eval);
218 impl<$($params)*, const $UPPER: $usize_e> $crate::Const for __Eval<$Self, $UPPER> $($($where_bounds)*)? {
219 type Type = $Output;
220 const VALUE: Self::Type = $EVAL;
221 }
222 $(#[$meta])*
223 impl<$($params)*> $crate::AcceptUpperBound for $Self $($($where_bounds)*)? {
224 type Output = $Output;
225 const DESIRED_GENERIC: $usize_d = $DESIRED_GENERIC;
226 type Eval<const $UPPER: $usize_e> = __Eval<Self, $UPPER>;
227 }
228 };
229 };
230}