ranged_integers/lib.rs
1//! # Ranged integers [nightly only]
2//!
3//! The crate provides [an integer type](struct.Ranged.html) restricted to a compile time defined range with
4//! automatic data size selection, automatic bounds calculation for arithmetics and the possibility
5//! of fixed-size array indexing and range iteration.
6//!
7//! The library relies to the unstable const generics-related features.
8//! It causes ICEs on some Rust toolchains and may once stop working.
9//! It will require nightly Rust until the `generic-const-exprs` feature is stabilized.
10//! Consider the other stable ranged integers crates, allowing to ensure the bounds and
11//! perform the arithmetic operations withing the type:
12//! - [deranged](https://crates.io/crates/deranged). It provides checked/saturating and unsafe
13//! unchecked arithmetics within the specified bounds, equalities and comparisons. There is a
14//! procedural macro to select a type of ranged based on literal bounds.
15//! - [light_ranged_integers](https://crates.io/crates/light-ranged-integers) provides
16//! a nice way to choose the type of arithmetic operation (wrapping/saturating/panicking)
17//! with the type-level mark.
18//!
19//! # Version info
20//!
21//! The version 0.11.0 was built for nightly-2026-01-31 toolchain.
22//!
23//! # Prerequisites
24//!
25//! The library usage requires the following Rust features enabled in the user crate or application:
26//!
27//! ```
28//! // Without this rustc generates errors and sometimes panics.
29//! #![feature(adt_const_params, generic_const_exprs)]
30//! ```
31//!
32//! Note that the features needed depend on the package version.
33//!
34//! Switch on the optimizer of the package for development builds:
35//!
36//! ```toml
37//! [profile.dev.package.ranged_integers]
38//! opt-level = 2
39//! ```
40//!
41//! # Usage and examples
42//!
43//! ## Ranged semantics
44//!
45//! Use `Ranged<MIN, MAX>` type to be sure of the value range:
46//!
47//! ```
48//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
49//! fn move_player(dice_roll: Ranged<1, 6>) { // Ranged<1, 6> is automatically of 1 byte
50//! let x : i32 = dice_roll.into(); // Conversion is allowed, i32 can store 1..=6
51//! }
52//! ```
53//!
54//! ## Contents
55//!
56//! * [Data layout paradigm](#data-layout-paradigm)
57//! * [Ranged and integer primitives](#ranged-and-integer-primitives)
58//! - [Creation of Ranged at compile time](#creation-of-ranged-at-compile-time)
59//! - [Ranged -> Ranged conversion](#ranged---ranged-conversion)
60//! - [int -> Ranged conversion](#int---ranged-conversion)
61//! - [Ranged -> int conversion](#ranged---int-conversion)
62//! * [Array indexing, slicing and iteration](#array-indexing-slicing-and-iteration)
63//! * [Comparison](#comparison)
64//! * [Arithmetics](#arithmetics)
65//! * [Pattern matching and case analysis](#pattern-matching-and-case-analysis)
66//!
67//! ## Data layout paradigm
68//!
69//! The [Ranged] automatically chooses the smallest size possible according to `MIN..=MAX` range.
70//! It supports i8, u8, i16, u16, i32, u32, i64 and u64 layouts (i128 and u128 are not supported),
71//! and a special zero-size layout for "constant" values with `MIN==MAX`.
72//!
73//! ```
74//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn main(){
75//! use core::mem::size_of;
76//! assert_eq!(size_of::< Ranged<42, 42> >(), 0); // It's always 42, no need to store it
77//!
78//! assert_eq!(size_of::< Ranged<-1, 127> >(), 1); // Fits i8
79//! assert_eq!(size_of::< Ranged<0, 200> >(), 1); // Fits u8
80//! assert_eq!(size_of::< Ranged<-1, 200> >(), 2); // Fits i16, doesn't fit i8 or u8
81//!
82//! assert_eq!(size_of::< Ranged<0, 90000> >(), 4); // The range fits i32
83//! # }
84//! ```
85//!
86//! The implementation heavily relies on the optimizer.
87//!
88//! ## Ranged and integer primitives
89//!
90//! ### Creation of `Ranged` at compile time
91//!
92//! The [`Ranged::create_const`] can be used to create a
93//! [`Ranged`] value checking it at compile time.
94//! The macro [`r!`] does the same but a bit shorter.
95//!
96//! ```
97//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
98//! // Way 1: specify the bounds explicitly
99//! move_player(Ranged::<1,6>::create_const::<4>());
100//! move_player(r!([1 6] 4)); // Same thing
101//!
102//! // Way 2: do not specify the bounds when possible
103//! move_player(Ranged::create_const::<4>());
104//! move_player(r!([] 4)); // Same thing
105//! let x: Ranged::<0, 100> = Ranged::create_const::<42>();
106//! let y: Ranged::<0, 100> = r!([] 42); // Same thing
107//!
108//! // Way 3: a special case with the single possible value
109//! let x = Ranged::<4, 4>::create_const::<4>();
110//! let y = r!(4); // Same thing
111//! ```
112//!
113//! It fails if the bounds are corrupted:
114//!
115//! ```compile_fail
116//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
117//! move_player(r!([] 7)); // Error: Can't store 7 in [1 6] interval
118//! ```
119//! ```compile_fail
120//! move_player(r!([1 7] 7)); // Error: type mismatch, move_player() requires Ranged<1, 6>
121//! ```
122//!
123//! ### `Ranged` -> `Ranged` conversion
124//!
125//! The `Ranged` can be converted to the type with different bounds using
126//! [`expand()`](struct.Ranged.html#method.expand) generic method (compile-time check)
127//! and the methods [`fit()`](struct.Ranged.html#method.fit), [`fit_min()`](struct.Ranged.html#method.fit_min),
128//! [`fit_max()`](struct.Ranged.html#method.fit_max) for runtime check. The `Ranged` values may be compared
129//! to each other yielding the shrunk bounds using
130//! [`fit_less_than()`](struct.Ranged.html#method.fit_less_than),
131//! [`fit_less_eq()`](struct.Ranged.html#method.fit_less_eq),
132//! [`fit_greater_than()`](struct.Ranged.html#method.fit_greater_than), and
133//! [`fit_greater_eq()`](struct.Ranged.html#method.fit_greater_eq) methods.
134//! [Pattern matching](#pattern-matching-and-case-analysis) functionality can be used to narrow bounds.
135//!
136//! ```
137//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
138//! let expandable: Ranged<4, 5> = r!([] 5); // Fits Ranged<1,6> accepted by move_player
139//! let overlapping: Ranged<4, 9> = r!([] 5); // Doesn't fit, but the value 5 is acceptable
140//! move_player(expandable.expand());
141//! move_player(overlapping.fit().unwrap());
142//! ```
143//!
144//! Shrinking with `expand()` is forbidden:
145//!
146//! ```compile_fail
147//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
148//! # let overlapping: Ranged<4, 9> = r!([] 5);
149//! move_player(overlapping.expand()); // Error: the bounds 4..=9 can't fit in 1..=6
150//! ```
151//!
152//! ### `int` -> `Ranged` conversion
153//!
154//! Way 1: ensure the bounds with [`Ranged::new(i128) -> Option<Ranged>`](struct.Ranged.html#method.new) function.
155//!
156//! **Note**: due to incomplete features limitations, the function may fail to compile with
157//! the 'trait bound is not satisfied' if the bounds are not explicitly specified.
158//!
159//! ```
160//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
161//! let some_int = 4;
162//! let some_wrong_int = 8;
163//! assert!(Ranged::<0, 6>::new(some_int) == Some(r!([0 6] 4)));
164//! assert!(Ranged::<0, 6>::new(some_wrong_int) == None);
165//!
166//! move_player(Ranged::new(some_int).unwrap()); // this call may fail to compile
167//! // use Ranged::<0, 6>::new instead
168//! ```
169//!
170//! Way 2: use the [`Remainder operation`](struct.Ranged.html#impl-Rem<Ranged<VAL%2C%20VAL>>) with the "const" divisor
171//!
172//! ```
173//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
174//! let x: Ranged<-9, 9> = 15_i32 % r!(10);
175//! let y: Ranged<0, 9> = 15_u32 % r!(10);
176//! assert!(x == r!(5));
177//! assert!(y == r!(5));
178//! ```
179//!
180//! Way 3: Convert the primitive types to `Ranged` with their native bounds using [`AsRanged`]
181//!
182//! ```
183//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
184//! use ranged_integers::AsRanged;
185//! let x = 15_u8.as_ranged(); // Ranged<0, 255>
186//! let y = 15_i16.as_ranged(); // Ranged<-32768, 32767>
187//! ```
188//!
189//! ### `Ranged` -> `int` conversion
190//!
191//! `int::From` trait is implemented when the value is proved to
192//! fit into the result type:
193//!
194//! ```
195//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
196//! let x = r!([0 200] 20);
197//! let y: u8 = x.into(); // 0..=200 fits u8
198//! ```
199//!
200//! ```compile_fail
201//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
202//! let x = r!([0 200] 20);
203//! let y: i8 = x.into(); // 0..=200 doesn't fit i8
204//! ```
205//!
206//! `From` and `Into` operations can't be used in const context.
207//! A set of [`const fn`](struct.Ranged.html#method.i8)s allows const conversions to
208//! any integer primitive except for `u128`:
209//!
210//! ```
211//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
212//! let x = r!([0 200] 20);
213//! let y = x.u8(); // y is u8
214//! let z = x.i16(); // z is i16
215//! let w = x.usize(); // w is usize
216//! ```
217//!
218//! ```compile_fail
219//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
220//! let x = r!([0 200] 20);
221//! let err = x.i8(); // Error: 0..=200 doesn't fit i8
222//! ```
223//!
224//! ## Array indexing, slicing and iteration
225//!
226//! The [`ConstInclusiveRange<MIN,MAX>`] zero-size type is a range `MIN..=MAX`
227//! capable to create the iterator ([`IntoIterator`] trait implemented)
228//! with `Ranged<MIN, MAX>` output type. The [`r!`] macro can be used instead.
229//!
230//! The [`Ranged::iter_up`](struct.Ranged.html#method.iter_up) method creates an
231//! iterator from the current value up to `MAX`.
232//!
233//! The arrays `[T; N]` may be indexed with `Ranged<0, {N-1}>` and sliced
234//! with `r!(MIN..END)` range with a reference to fixed-size array output.
235//!
236//! ```
237//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
238//! let arr = [r!([1 6] 2), r!([] 3), r!([] 4), r!([] 5)];
239//!
240//! assert_eq!(arr[r!(1..=2)], [3,4]); // Slicing with array reference output
241//! assert_eq!(arr[r!([0 3] 1)], 3); // Slicing with array reference output
242//!
243//! // Not recommended to use this:
244//! for i in ConstInclusiveRange::<0, 3> {
245//! move_player(arr[i]) // iters through 0,1,2,3
246//! }
247//! for i in r!(0..=3) {
248//! move_player(arr[i]) // iters through 0,1,2,3
249//! }
250//! for i in r!(0..=3) {
251//! move_player(arr[i]) // iters through 0,1,2,3
252//! }
253//! for mv in r!([1 6] 3).iter_up() {
254//! move_player(mv) // calls with 3,4,5,6
255//! }
256//! ```
257//!
258//! ## Comparison
259//!
260//! All `Eq` and `Ord` operations between different Ranged types are allowed,
261//! so as `Ranged` vs integer comparisons:
262//!
263//! ```
264//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
265//! assert!(r!([1 6] 4) == r!([1 10] 4));
266//! assert!(r!([1 6] 4) != r!([1 6] 5));
267//! assert!(r!(4) == 4);
268//! assert!(5 != r!([1 6] 4));
269//!
270//! assert!(r!(5) > r!([1 6] 4));
271//! assert!(4 < r!([1 6] 5));
272//! ```
273//!
274//! To constrain the output type ruled by comparison, one may use [`Ranged::fit_less_than`]
275//! function and its siblings [`Ranged::fit_less_eq`], [`Ranged::fit_greater_than`], and [`Ranged::fit_greater_eq`].
276//!
277//! ## Arithmetics
278//!
279//! The bounds of arithmetic operations results are automatically recalculated.
280//!
281//! Currently supported:
282//! * The basic arithmetic operations (+, -, *, /), not available in const context
283//! * The const arithmetic functions ([`add`](Ranged::add), [`sub`](Ranged::sub), [`mul`](Ranged::mul), [`div`](Ranged::div))
284//! * [`div_euclid`](Ranged::div_euclid) and [`rem_euclid`](Ranged::div_euclid)
285//! * [`min`](Ranged::min) and [`max`](Ranged::max)
286//! * [`abs`](Ranged::abs) and [`neg`](Ranged::neg)
287//!
288//! ```
289//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
290//! let x = r!([1 6] 5);
291//! let y = r!([1 6] 4);
292//!
293//! let a = x + y; // The minimum is (1+1)=2, the maximum is (6+6)=12
294//! let check_add: Ranged<2, 12> = a; // Range assertion assignment
295//! assert_eq!(check_add, r!(9));
296//!
297//! let s = x - y; // The minimum is (1-6)=-5, the maximum is (6-1)=5
298//! let check_sub: Ranged<-5, 5> = s; // Range assertion assignment
299//! assert_eq!(check_sub, r!(1));
300//!
301//! let m = x * y; // The minimum is (1*1)=1, the maximum is (6*6)=36
302//! let check_mul: Ranged<1, 36> = m; // Range assertion assignment
303//! assert_eq!(check_mul, r!(20));
304//!
305//! let d = x / y; // The minimum is (1/6)=0, the maximum is (6/1)=6
306//! let check_div: Ranged<0, 6> = d; // Range assertion assignment
307//! assert_eq!(check_div, r!(1));
308//!
309//! let r = x % y;
310//! let check_rem: Ranged<0, 5> = r; // Range assertion assignment
311//! assert_eq!(check_rem, r!(1));
312//!
313//! let n = -x;
314//! let check_neg: Ranged<-6, -1> = n; // Range assertion assignment
315//! assert_eq!(check_neg, r!(-5));
316//!
317//! let min: Ranged<1,6> = x.min(a);
318//! let max: Ranged<2,12> = x.max(a);
319//! let abs: Ranged<0,6> = r!([-1 6] -1).abs();
320//! let neg: Ranged<-6,1> = r!([-1 6] -1).neg();
321//! ```
322//!
323//! The division and remainder are allowed only if it's impossible to store "0" in the divisor:
324//!
325//! ```compile_fail
326//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
327//! let x = r!([1 6] 4);
328//! let y = r!([0 6] 5);
329//! let z = r!([-1 6] 5);
330//!
331//! let d = x / y; // Error: y can be 0
332//! let e = x % z; // Error: z can be 0
333//! ```
334//!
335//! The true bounds calculation routine for `Rem` operation is far too complex.
336//! In this library the calculated bounds will never exceed `1-DMAXABS..=DMAXABS-1` where `DMAXABS` is the
337//! maximum of the divisor absolute value.
338//!
339//! This kind of `Rem` followed by `expand` is available for any dividend:
340//! ```
341//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
342//! let x = r!([-1000 1000] 500);
343//! let y = r!([-1 1000] 500);
344//! let d = r!([1 10] 7);
345//!
346//! let r: Ranged<-9, 9> = (x%d).expand();
347//! // In this case, it expands just from Ranged<-9, 9> to itself
348//!
349//! let r: Ranged<-9, 9> = (y%d).expand();
350//! // In this case, it expands from Ranged<-1, 9>
351//! ```
352//!
353//! But the actual calculation routine can produce smaller bounds:
354//!
355//! ```
356//! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {}
357//! // In the general case the output is Ranged<1-MAX, MAX-1>, MAX from divisor (by absolute value)
358//! let x: Ranged<-9, 9> = (r!([-1000 1000] 500) % r!([1 10] 7));
359//! let x: Ranged<-9, 9> = (r!([-1000 1000] 500) % r!([-10 -1] -7));
360//!
361//! // If the dividend is nonnegative or nonpositive,
362//! // the output range is limited to 0 from one side.
363//! let x: Ranged<0, 9> = r!([0 100] 15) % r!(10);
364//! let x: Ranged<-9, 0> = r!([-100 0] -15) % r!(10);
365//!
366//! // The limit can't exceed the dividend's MIN(if negative) or MAX(if positive):
367//! let x: Ranged<-10, 10> = r!([-10 10] 4) % r!([1 1000] 70);
368//!
369//! // If the divisor is "constant", the output bounds are the true bounds:
370//! let x: Ranged<4, 7> = r!([14 17] 15) % r!(10);
371//!
372//! // In particular, if both operands are "constant", the result is "constant"
373//! let x: Ranged<5, 5> = r!(15) % r!(10);
374//! ```
375//!
376//! Following these rules, the calculated bounds may be wider than the true ones, like
377//! `Ranged<36289, 36292> % Ranged<6, 9> = Ranged<0, 8>` while the
378//! result never exceeds `Ranged<1, 4>`.
379//!
380//! ## Pattern matching and case analysis
381//!
382//! Use [`rsplit!`] macro to perform the case analysis of `Ranged`. Provided
383//! the bounds of the cases, it narrows `Ranged` down to the specified pattern.
384//!
385//! A limited version of pattern matching is implemented with [`rmatch!`] macro.
386//! It does not narrow down the bounds, but supports complex Rust patterns.
387//!
388//! Split the range into lower and higher parts with [`Ranged::split`] function.
389//! The bounds are narrowed according to comparison result with a constant.
390//!
391//! The function [`Ranged::split_subtract`] compares the difference of two `Ranged`
392//! values with zero], and narrows down all three values (minuend, subtrahend and
393//! difference) according to the result.
394//!
395
396
397#![no_std]
398#![allow(incomplete_features)]
399
400#![feature(generic_const_exprs)] // The whole idea is based on this
401#![feature(adt_const_params)] // This is mostly for OperationPossibility
402#![feature(specialization)] // It allows to decrease a number of constraints in Ranged
403#![feature(freeze)] // If it is enabled in user crate, the Ranged should implement Freeze trait
404
405#![deny(missing_docs)]
406#![deny(clippy::nursery)]
407#![warn(clippy::pedantic)]
408#![feature(const_index)]
409#![feature(const_trait_impl)]
410
411// An alias integer representing the public interface of Ranged constants. Introduced
412// to easily change when necessary.
413#[allow(non_camel_case_types)]
414type irang = i128;
415
416
417#[cfg(test)] #[macro_use] extern crate std;
418#[cfg(test)] mod tests;
419
420pub mod value_check; // Compile-time infrastructure for Ranged
421
422mod holder; // Internal representation of ranged
423mod conversions; // Conversions to/from Ranged
424mod arithmetics; // Arithmetic operations
425mod iter; // Iterating over a constant range
426mod arrays; // Implementing Ranged-related logics for arrays indexing and slicing
427
428pub use conversions::{AsRanged, Split, SplitByDifference};
429pub use iter::ConstInclusiveRange;
430
431use value_check::{Assert, IsAllowed, OperationPossibility, memlayout, allow_range, allow_if, allow_creation};
432
433/// A value restricted to the given bounds
434#[repr(transparent)]
435#[derive(Clone, Copy, Hash)]
436pub struct Ranged<const MIN: irang, const MAX: irang>
437where
438 // This restriction kills two birds with one stone: first, it forbids to
439 // work with the Ranged beyond u64 or i64 layout, second, it bounds the
440 // memlayout(MIN, MAX) constant, so it can be used in the inner type. All
441 // the generic calls to Ranged<...> have to satisfy this constraint.
442 Assert<{allow_range(memlayout(MIN, MAX))}>: IsAllowed,
443{
444 v: holder::RangedRepr<{ memlayout(MIN, MAX) }>,
445}
446
447
448impl<const MIN: irang, const MAX: irang> Ranged<MIN, MAX>
449where
450 Assert<{allow_range(memlayout(MIN, MAX))}>: IsAllowed,
451{
452 /// Create a Ranged value without checking the bounds.
453 ///
454 /// # Safety
455 ///
456 /// The value (parameter `n`) must be inside the inclusive range `MIN..=MAX`.
457 /// Having an integer outside the bounds is *[undefined behavior]*.
458 ///
459 /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
460 #[allow(clippy::inline_always)] #[must_use] #[inline(always)]
461 pub const unsafe fn unchecked_new(n: irang) -> Self {
462 Self {
463 v: holder::RangedRepr::from_irang(n),
464 }
465 }
466
467 // Convert Ranged to a primitive
468 #[allow(clippy::inline_always)] #[must_use] #[inline(always)]
469 const fn get(self) -> irang {
470 if MIN == MAX {MIN}
471 else {self.v.to_irang(const{MIN>=0})}
472 }
473
474 /// Create a Ranged value checking the bounds at runtime
475 ///
476 /// **Note**: due to incomplete features limitations, the function may fail to compile with
477 /// the 'trait bound is not satisfied' if the bounds are not explicitly specified.
478 ///
479 /// # Example
480 ///
481 /// ```
482 /// # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
483 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
484 /// let input = "42".to_string();
485 /// let user_input = input.parse()?;
486 /// if let Some(input) = Ranged::<1, 100>::new(user_input){
487 /// println!("The value is in range 1..=100")
488 /// }
489 /// else {
490 /// println!("The value is too high :(")
491 /// }
492 /// # Ok(()) }
493 /// ```
494 #[must_use]
495 pub const fn new(n: irang) -> Option<Self> {
496 if (MIN <= n) && (n <= MAX) {
497 Some(unsafe { Self::unchecked_new(n) })
498 } else {
499 None
500 }
501 }
502}
503
504impl<const MIN: irang, const MAX: irang> Ranged<MIN, MAX>
505where
506 Assert<{allow_range(memlayout(MIN, MAX))}>: IsAllowed,
507{
508 /// Create a Ranged constant checking the bounds at compile time
509 ///
510 /// Consider using [`r!`] macro instead
511 ///
512 /// # Example
513 ///
514 /// ```
515 /// # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
516 /// let a = Ranged::<0, 100>::create_const::<42>();
517 /// let a = r!([0 100] 42);
518 /// ```
519 #[must_use]
520 pub const fn create_const<const V: irang>() -> Self
521 where Assert<{ allow_creation(MIN, V, MAX) }>: IsAllowed,
522 {
523 unsafe { Self::unchecked_new(V) }
524 }
525
526 /// Iterate up from current value to `Self::MAX` (inclusively) using `Self` as output
527 #[must_use]
528 pub const fn iter_up(self) -> iter::Iter<MIN, MAX> {
529 iter::Iter::<MIN,MAX>{current: Some(self)}
530 }
531}
532
533/// Create a ranged value or a range at compile time
534///
535/// **Warning**: ensure `#![feature(adt_const_params)]` is enabled.
536///
537/// # Example
538///
539/// ```
540/// # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
541/// // Explicit bounds:
542/// let a = r!([0 42] 23); // Ranged<0, 42> with a value 23
543/// // Type inference:
544/// let b: Ranged<0, 100> = r!([] 42); // Ranged<0, 100> with a value 42
545/// // "Constant" value:
546/// let c = r!(10); // Zero-sized Ranged<10, 10> with a value 10
547/// //Range:
548/// for i in r!(0..=9){
549/// let v: Ranged<0,9> = i;
550/// }
551/// ```
552#[macro_export]
553macro_rules! r {
554 ([$min:literal $max:literal] $x:expr) => {
555 $crate::Ranged::<$min, $max>::create_const::<$x>()
556 };
557 ([] $v:expr) => {
558 $crate::Ranged::create_const::<$v>()
559 };
560 (-$min:tt..=$max:tt) => {
561 $crate::ConstInclusiveRange::<{-$min}, {$max}>
562 };
563 (-$min:tt..=-$max:tt) => {
564 $crate::ConstInclusiveRange::<{-$min}, {-$max}>
565 };
566 ($min:tt..=$max:tt) => {
567 $crate::ConstInclusiveRange::<{$min}, {$max}>
568 };
569 ($v:literal) => {
570 $crate::Ranged::<$v, $v>::create_const::<$v>()
571 };
572 ($v:tt) => {
573 $crate::Ranged::<$v, $v>::create_const::<{$v}>()
574 };
575}
576
577impl<const MIN: irang, const MAX: irang> core::fmt::Display for Ranged<MIN, MAX>
578where Assert<{allow_range(memlayout(MIN, MAX))}>: IsAllowed,
579{
580 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
581 write!(f, "{}", self.get())
582 }
583}
584
585impl<const MIN: irang, const MAX: irang> core::fmt::Debug for Ranged<MIN, MAX>
586where Assert<{allow_range(memlayout(MIN, MAX))}>: IsAllowed,
587{
588 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
589 if MIN == MAX {
590 write!(f, "r!({MIN})")
591 } else {
592 write!(f, "r!([{MIN} {MAX}] {})", self.get())
593 }
594 }
595}
596
597
598/// Ranged pattern matching macro: no bounds recalculation, extended patterns
599///
600/// Allows to match a [`Ranged`] value over a range it covers. The feature has
601/// the following limitations:
602/// - The bounds must be explicitly specified; they are checked, but not inferred
603/// - The macro syntax supports a subset of Rust pattern matching syntax
604/// - `i128::MIN` and `i128::MAX` values must not be in range
605/// - The unclear error reporting
606///
607/// ```
608/// # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
609/// fn ranged_to_bool(r: Ranged<0,1>) -> bool {
610/// rmatch!{[0 1] r // Bounds and expression (token tree,
611/// // complex expressions must be in parentheses)
612/// 0 => {false}
613/// 1 => {true} // Complex patterns like 1..5 | 12..14 are supported
614/// }
615/// }
616/// ```
617#[macro_export]
618macro_rules! rmatch {
619 ([$min:literal $max:literal] $val:tt
620 $(
621 $p:pat => { $e:expr }
622 )*
623 ) => {
624 {
625 #[allow(renamed_and_removed_lints)]
626 #[deny(const_err)]
627 const _MINF: i128 = $min - 1;
628 #[allow(renamed_and_removed_lints)]
629 #[deny(const_err)]
630 const _PINF: i128 = $max + 1;
631 let _v: Ranged<$min, $max> = $val;
632 match _v.i128() {
633 i128::MIN..=_MINF => unsafe {core::hint::unreachable_unchecked()}
634 _PINF..=i128::MAX => unsafe {core::hint::unreachable_unchecked()}
635 $( $p => { $e } )*
636 }
637 }
638 };
639}
640
641/// Ranged pattern matching macro: bounds recalculation, strictly inclusive ranges as patterns
642///
643/// Allows to match a [`Ranged`] value over a range it covers. The feature has
644/// the following limitations:
645/// - The bounds must be explicitly specified; they are checked, but not inferred
646/// - The macro syntax supports only the inclusive ranges patterns. Use X..=X for
647/// a single value pattern
648/// - The unclear error reporting
649///
650/// ```
651/// # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
652/// fn to_upper_half(r: Ranged<1,6>) -> Ranged<4,6> {
653/// // turn 1 to 4, 2 to 5, 3 to 6
654///
655/// rsplit!{[1 6] r // Bounds and expression (token tree,
656/// // complex expressions must be in parentheses)
657/// 1..=3 low => {low + r!(3)} // inside the expression, low is set to Ranged<0, 3>
658/// 4..=6 high => {high} // inside the expression, high is set to Ranged<4, 6>
659/// }
660/// }
661/// ```
662#[macro_export]
663macro_rules! rsplit {
664 ([$min:literal $max:literal] $val:tt
665 $(
666 $minval:literal..=$maxval:literal $varame:ident => $e:block
667 )*
668 ) => {
669 {
670 #[allow(renamed_and_removed_lints)]
671 #[deny(const_err)]
672 const _MINF: i128 = $min - 1;
673 #[allow(renamed_and_removed_lints)]
674 #[deny(const_err)]
675 const _PINF: i128 = $max + 1;
676 let _v: Ranged<$min, $max> = $val;
677 match _v.i128() {
678 i128::MIN..=_MINF => unsafe {core::hint::unreachable_unchecked()}
679 _PINF..=i128::MAX => unsafe {core::hint::unreachable_unchecked()}
680 $( $minval..=$maxval => {
681 let $varame: Ranged<$minval, $maxval> = unsafe{ $crate::Ranged::<$minval, $maxval>::unchecked_new(_v.i128()) };
682 $e
683 } )*
684 }
685 }
686 };
687}
688
689// Failtests: the tests that should fail or generate an error.
690
691
692#[allow(dead_code)]
693#[doc(hidden)]
694/**
695```
696# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
697u8::from(r!(0));
698```
699
700```compile_fail
701# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
702u8::from(r!(-1));
703```
704
705```
706# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
707u8::from(r!(255));
708```
709
710```compile_fail
711# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
712u8::from(r!(256));
713```
714
715```
716# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
717i8::from(r!(-128));
718```
719
720
721```compile_fail
722# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
723i8::from(r!(-129));
724```
725
726```
727# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
728i8::from(r!(127));
729```
730
731
732```compile_fail
733# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
734i8::from(r!(128));
735```
736
737
738```
739# #![feature(adt_const_params, generic_const_exprs)]
740# #[macro_use] extern crate ranged_integers; use ranged_integers::*;
741let a = r![[100 1000] 500] / r![[1 6] 5];
742```
743```compile_fail
744# #![feature(adt_const_params, generic_const_exprs)]
745# #[macro_use] extern crate ranged_integers; use ranged_integers::*;
746let a = r![[100 1000] 500] / r![[0 6] 5];
747```
748```compile_fail
749# #![feature(adt_const_params, generic_const_exprs)]
750# #[macro_use] extern crate ranged_integers; use ranged_integers::*;
751let a = r![[100 1000] 500] / r![[-1 6] 5];
752```
753
754```
755# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
756Ranged::<0,1>::new(1);
757```
758
759
760```compile_fail
761# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
762Ranged::<1,0>::new(1);
763```
764
765
766```
767# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
768let x: Ranged::<0,1> = Ranged::<0,1>::new(1).unwrap();
769```
770
771
772```
773# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
774let x: Ranged::<0,1> = Ranged::<0,1>::new(1).unwrap();
775```
776
777
778```
779# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
780let a = r!([-10 10] 0);
781rmatch!{[-10 10] a
782 _ => {()}
783}
784```
785
786```compile_fail
787# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
788let a = r!([-170141183460469231731687303715884105728 10] 0);
789rmatch!{[-170141183460469231731687303715884105728 10] a
790 _ => {()}
791}
792```
793
794```compile_fail
795# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
796let a = r!([-10 170141183460469231731687303715884105727] 0);
797rmatch!{[-10 170141183460469231731687303715884105727] a
798 _ => {()}
799}
800```
801
802```
803# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
804assert!( r!([10 50] 30).fit_less_than(r!([10 45] 40)) == Some(r!([10 44] 30)) );
805```
806
807```
808# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
809assert_eq!(r!([10 50] 39).fit_less_than( r!([0 45] 40) ), Some(r!([10 44] 39)));
810```
811
812```
813# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
814assert_eq!(r!([10 50] 39).fit_less_than( r!([30 45] 40) ), Some(r!([10 44] 39)));
815```
816
817```
818# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
819assert_eq!(r!([10 50] 40).fit_less_than( r!([30 45] 40) ), None);
820```
821
822```compile_fail
823# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
824assert_eq!(r!([10 40] 39).fit_less_than( r!([30 45] 40) ), Some(r!([10 44] 39)));
825```
826
827```compile_fail
828# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
829assert_eq!(r!([45 50] 47).fit_less_than( r!([0 45] 40) ), None);
830```
831
832```compile_fail
833# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
834assert_eq!(r!([10 20] 10).fit_less_than( r!([30 45] 40) ), Some(r!([10 45] 10)));
835```
836
837
838
839```
840# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
841assert!( r!([10 50] 40).fit_less_eq(r!([10 45] 40)) == Some(r!([10 45] 40)) );
842```
843
844```
845# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
846assert_eq!(r!([10 50] 40).fit_less_eq( r!([0 45] 40) ), Some(r!([10 45] 40)));
847```
848
849```
850# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
851assert_eq!(r!([10 50] 40).fit_less_eq( r!([30 45] 40) ), Some(r!([10 45] 40)));
852```
853
854```
855# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
856assert_eq!(r!([10 50] 40).fit_less_eq( r!([40 45] 40) ), Some(r!([10 45] 40)));
857```
858
859```
860# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
861assert_eq!(r!([10 50] 41).fit_less_eq( r!([30 45] 40) ), None);
862```
863
864```compile_fail
865# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
866assert!( r!([10 40] 40).fit_less_eq(r!([40 45] 40)) == Some(r!([10 40] 40)) );
867```
868```
869# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
870assert!( r!([10 50] 10).fit_less_eq(r!([5 10] 10)) == Some(r!([10 10] 10)) );
871```
872
873
874```
875# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
876assert!( r!([10 50] 40).fit_greater_than(r!([20 40] 39)) == Some(r!([21 50] 40)) );
877```
878
879```
880# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
881assert!( r!([10 50] 40).fit_greater_than(r!([20 50] 39)) == Some(r!([21 50] 40)) );
882```
883
884```
885# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
886assert!( r!([10 50] 40).fit_greater_than(r!([20 100] 39)) == Some(r!([21 50] 40)) );
887```
888
889```
890# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
891assert_eq!(r!([10 50] 40).fit_greater_than( r!([30 45] 40) ), None);
892```
893
894```compile_fail
895# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
896assert!( r!([10 50] 40).fit_greater_than(r!([9 40] 39)) == Some(r!([10 50] 40)) );
897```
898
899```compile_fail
900# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
901assert!( r!([10 50] 50).fit_greater_than(r!([50 55] 50)) == Some(r!([50 50] 50)) );
902```
903
904```compile_fail
905# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
906assert!( r!([10 50] 40).fit_greater_than(r!([0 10] 5)) == Some(r!([10 50] 40)) );
907```
908
909
910```
911# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
912assert!( r!([10 50] 40).fit_greater_eq(r!([20 40] 40)) == Some(r!([20 50] 40)) );
913```
914
915```
916# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
917assert!( r!([10 50] 40).fit_greater_eq(r!([20 50] 40)) == Some(r!([20 50] 40)) );
918```
919
920```
921# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
922assert!( r!([10 50] 40).fit_greater_eq(r!([20 100] 40)) == Some(r!([20 50] 40)) );
923```
924
925```
926# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
927assert_eq!(r!([10 50] 40).fit_greater_eq( r!([30 45] 41) ), None);
928```
929
930```compile_fail
931# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
932assert!( r!([10 50] 40).fit_greater_eq(r!([10 40] 39)) == Some(r!([10 50] 40)) );
933```
934
935```
936# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
937assert!( r!([10 50] 50).fit_greater_eq(r!([50 55] 50)) == Some(r!([50 50] 50)) );
938```
939
940```compile_fail
941# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
942assert!( r!([10 50] 40).fit_greater_eq(r!([0 10] 5)) == Some(r!([10 50] 40)) );
943```
944
945
946
947```compile_fail
948# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
949assert!( r!([10 50] 40).fit_greater_eq(r!([0 10] 5)) == Some(r!([10 50] 40)) );
950```
951
952
953
954```
955# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
956let arr = [1,2,3,4,5];
957let x = &arr[r!(4).expand()];
958assert_eq!(*x, 5);
959```
960
961```compile_fail
962# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
963let arr = [1,2,3,4,5];
964let x = &arr[r!(5).expand()];
965```
966
967```
968# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
969let arr = [1,2,3,4,5];
970let x = &arr[r!(4..=4)];
971assert_eq!(*x, [5]);
972```
973
974```compile_fail
975# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
976let arr = [1,2,3,4,5];
977let x: &[u8; 1] = &arr[r!(4..=5)];
978```
979
980
981```
982# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
983r!([1 3] 3).split_subtract(r!([2 4] 3));
984```
985
986
987```compile_fail
988# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
989r!([1 3] 3).split_subtract(r!([3 4] 3));
990```
991
992
993```compile_fail
994# #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*;
995r!([1 2] 2).split_subtract(r!([3 4] 3));
996```
997
998*/
999struct Failtests;
1000
1001
1002// Compile-time test for sizes and alignments of Ranged
1003#[allow(dead_code)]
1004const SIZE_ALIGN_CHECK: () = {
1005 use core::mem::{align_of, size_of};
1006
1007 macro_rules! sz_align {
1008 ($sz:ty, $t:ty) => {
1009 assert!(size_of::<$t>() == size_of::<$sz>());
1010 assert!(align_of::<$t>() == align_of::<$sz>());
1011 };
1012 }
1013
1014 sz_align!((), Ranged<0,0>);
1015 sz_align!((), Ranged<1,1>);
1016 sz_align!((), Ranged<-1,-1>);
1017 sz_align!((), Ranged<100, 100>);
1018 sz_align!((), Ranged<-100, -100>);
1019 sz_align!((), Ranged<500, 500>);
1020 sz_align!((), Ranged<-500, -500>);
1021 sz_align!((), Ranged<100_000, 100_000>);
1022 sz_align!((), Ranged<-100_000, -100_000>);
1023 sz_align!((), Ranged<10_000_000_000, 10_000_000_000>);
1024 sz_align!((), Ranged<-10_000_000_000, -10_000_000_000>);
1025
1026 sz_align!((), Ranged<-32768, -32768>);
1027 sz_align!((), Ranged<32767, 32767>);
1028 sz_align!((), Ranged<65535, 65535>);
1029 sz_align!((), Ranged<65536, 65536>);
1030
1031 sz_align!(i8, Ranged<10,11>);
1032 sz_align!(i8, Ranged<254,255>);
1033 sz_align!(i8, Ranged<126,127>);
1034 sz_align!(i8, Ranged<-128, -127>);
1035 sz_align!(i8, Ranged<0,10>);
1036 sz_align!(i8, Ranged<0,127>);
1037 sz_align!(i8, Ranged<0,255>);
1038 sz_align!(i8, Ranged<127,255>);
1039 sz_align!(i8, Ranged<-128, 127>);
1040
1041 sz_align!(i16, Ranged<-128, 128>);
1042
1043 sz_align!(i16, Ranged<-32768, 32767>);
1044 sz_align!(i16, Ranged<0, 32768>);
1045 sz_align!(i16, Ranged<0, 65535>);
1046 sz_align!(i16, Ranged<-32768, -32767>);
1047 sz_align!(i16, Ranged<32766, 32767>);
1048 sz_align!(i16, Ranged<65534, 65535>);
1049
1050 sz_align!(i32, Ranged<-32768, 32768>);
1051 sz_align!(i32, Ranged<0, 65536>);
1052
1053 sz_align!(i32, Ranged<0, 4_294_967_295>);
1054 sz_align!(i32, Ranged<-2_147_483_648, 2_147_483_647>);
1055 sz_align!(i32, Ranged<100, 10_000_000>);
1056 sz_align!(i32, Ranged<-100, 10_000_000>);
1057 sz_align!(i32, Ranged<100, 2_147_483_647>);
1058 sz_align!(i32, Ranged<-100, 2_147_483_647>);
1059
1060 sz_align!(i64, Ranged<-1, 4_294_967_295>);
1061 sz_align!(i64, Ranged<0, 4_294_967_296>);
1062 sz_align!(i64, Ranged<-2_147_483_649, 2_147_483_647>);
1063 sz_align!(i64, Ranged<-2_147_483_648, 2_147_483_648>);
1064
1065 sz_align!(i64, Ranged<0, 18_446_744_073_709_551_615>);
1066 sz_align!(i64, Ranged<-9_223_372_036_854_775_808, 9_223_372_036_854_775_807>);
1067};