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}