notzero/
lib.rs

1//! Provides the [`nz`] macro for constructing a 
2//! `std::num::NonZero*` from an integer constant.
3//! 
4//! The [`nz`] macro can infer the argument's type from 
5//! the inferred `NonZero*` return type,
6//! while some alternative macros always require that 
7//! the argument's type is specified.
8//! 
9//! # Example
10//! 
11//! ### Usage
12//! 
13//! ```rust
14//! use notzero::nz;
15//! use std::num::NonZeroU64;
16//! 
17//! let two = nz!(2u16); // returns a `NonZeroU16`
18//! assert_eq!(two.get(), 2u16);
19//! 
20//! // infers the argument's type from the returned `NonZero`
21//! const THREE: NonZeroU64 = nz!(3); 
22//! assert_eq!(THREE.get(), 3u64);
23//! 
24//! const FOUR: i8 = -4;
25//! let fourz = nz!(FOUR); // returns a `NonZeroI8`
26//! assert_eq!(fourz.get(), -4i8);
27//! ```
28//! 
29//! ### Zero argument
30//! 
31//! ```compile_fail
32//! const ZERO: u8 = 0;
33//! let _ = notzero::nz!(ZERO);
34//! ```
35//! the above code produces this compile-time error:
36//! ```text
37//! error[E0080]: evaluation of `main::_doctest_main_src_lib_rs_27_0::{constant#0}` failed
38//!  --> src/lib.rs:32:9
39//!   |
40//! 8 | let _ = notzero::nz!(ZERO);
41//!   |         ^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'passed in a `0` argument', src/lib.rs:8:9
42//!   |
43//!   = note: this error originates in the macro `notzero::nz` (in Nightly builds, run with -Z macro-backtrace for more info)
44//! ```
45//! 
46//! # No-std support
47//!
48//! `notzero` is `#![no_std]`, it can be used anywhere Rust can be used.
49//! 
50//! # Minimum Supported Rust Version
51//! 
52//! This crate requires Rust 1.79.0 because it uses `const { ... }` expressions
53//! (also known as "inline const").
54//! 
55//! [`nz`]: crate::nz
56#![no_std]
57
58#![deny(unused_results)]
59#![deny(clippy::missing_safety_doc)]
60#![deny(missing_debug_implementations)]
61#![deny(missing_docs)]
62#![deny(rustdoc::broken_intra_doc_links)]
63
64use typewit::TypeEq;
65
66#[cfg(test)]
67mod tests;
68
69
70/// Constructs a `std::num::NonZero*` from an integer constant.
71/// 
72/// # Type Inference
73/// 
74/// This macro can infer the argument's type from the return type and vice-versa,
75/// so you can do either of:
76/// - `let _ = nz!(1u8);`
77/// - `let _: NonZeroU8 = nz!(1);`
78/// 
79/// # Compile-time errors
80/// 
81/// This macro checks that the `$int` argument is non-zero at compile-time,
82/// producing a compile-time error if it is `0`.
83/// 
84/// # Example
85/// 
86/// ### Usage
87/// 
88/// ```rust
89/// use notzero::nz;
90/// use std::num::NonZeroU64;
91/// 
92/// let two = nz!(2u16); // returns a `NonZeroU16`
93/// assert_eq!(two.get(), 2u16);
94/// 
95/// // infers the argument's type from the returned `NonZero`
96/// let THREE: NonZeroU64 = nz!(3); 
97/// assert_eq!(THREE.get(), 3u64);
98/// ```
99/// 
100/// ### Zero argument
101/// 
102/// ```compile_fail
103/// let _ = notzero::nz!(0);
104/// ```
105/// the above code produces this compile-time error:
106/// ```text
107/// error[E0080]: evaluation of `main::_doctest_main_src_lib_rs_77_0::{constant#0}` failed
108///  --> src/lib.rs:81:9
109///   |
110/// 7 | let _ = notzero::nz!(0);
111///   |         ^^^^^^^^^^^^^^^ the evaluated program panicked at 'passed in a `0` argument', src/lib.rs:7:9
112///   |
113///   = note: this error originates in the macro `notzero::nz` (in Nightly builds, run with -Z macro-backtrace for more info)
114/// ```
115#[macro_export]
116macro_rules! nz {
117    ($int:expr) => {
118        const { $crate::__nonzero_new_unwrap($int) }
119    };
120}
121
122
123macro_rules! call_back_with_integer_types {
124    ($callback:ident) => {
125        $callback!{
126            (   NonZeroI8,    i8)
127            (  NonZeroI16,   i16)
128            (  NonZeroI32,   i32)
129            (  NonZeroI64,   i64)
130            ( NonZeroI128,  i128)
131            (NonZeroIsize, isize)
132            (   NonZeroU8,    u8)
133            (  NonZeroU16,   u16)
134            (  NonZeroU32,   u32)
135            (  NonZeroU64,   u64)
136            ( NonZeroU128,  u128)
137            (NonZeroUsize, usize)
138        }
139    }
140}
141
142
143
144macro_rules! declare_withness_stuff {
145    ( $(($nz_ty:ident, $int_ty:ident))* ) => {
146        use core::num::{$($nz_ty),*};
147
148        #[doc(hidden)]
149        pub trait __Integer: Copy + 'static {
150            type NonZero: __NonZeroInt<Zeroable = Self> + Copy + 'static;
151
152            #[doc(hidden)]
153            const __WITNESS: __IntegerWit<Self>;
154        }
155
156        #[doc(hidden)]
157        pub trait __NonZeroInt: Copy + 'static {
158            type Zeroable: __Integer<NonZero = Self> + Copy + 'static;
159        }
160
161        typewit::type_fn!{
162            #[doc(hidden)]
163            pub struct __ToNonZeroFn;
164
165            impl<I: __Integer> I => I::NonZero;
166        }
167
168
169        #[allow(missing_debug_implementations)]
170        #[doc(hidden)]
171        #[non_exhaustive]
172        #[allow(non_camel_case_types)]
173        pub enum __IntegerWit<W> {
174            $(
175                $int_ty(TypeEq<W, $int_ty>),
176            )*
177        }
178
179        $(
180            impl __Integer for $int_ty {
181                type NonZero = $nz_ty;
182
183                #[doc(hidden)]
184                const __WITNESS: __IntegerWit<Self> = __IntegerWit::$int_ty(TypeEq::NEW);
185            }
186            impl __NonZeroInt for $nz_ty {
187                type Zeroable = $int_ty;
188            }
189        )*
190    };
191}
192
193call_back_with_integer_types!{ declare_withness_stuff }
194
195
196
197#[doc(hidden)]
198#[track_caller]
199pub const fn __nonzero_new_unwrap<I, NZ>(n: I) -> NZ
200where
201    I: __Integer<NonZero = NZ>,
202    NZ: __NonZeroInt<Zeroable = I>
203{
204    macro_rules! __inner {
205        ( $(($nz_ty:ident, $int_ty:ident))* ) => {
206            match I::__WITNESS {
207                $(
208                    __IntegerWit::$int_ty(te) => {
209                        let Some(ret) = $nz_ty::new(te.to_right(n)) 
210                        else { panic!("passed in a `0` argument") };
211
212                        te.map(__ToNonZeroFn).to_left(ret)
213                    }
214                )*
215            }
216        }
217    }
218
219    call_back_with_integer_types!{__inner}
220}