nz 0.1.4

Collection of 100% safe macros for creating numeric core::num::NonZero{Integer} types more easily.
Documentation

nz

crates.io docs license rust msrv safety

The nz crate provides a collection of macros that simplify the creation of new instances of non-zero numeric types implemented in core::num. With these macros, you can easily generate constants of such numeric types using literals, constant values or expressions, all at compile time.

Features

  • No unsafe code
  • No dependencies
  • no_std compatible
  • Supports all core::num::NonZero{Integer} types
  • Compile time evaluation
  • Zero detection at compile time

Macros

Type Macro
NonZeroI8 nz::i8!
NonZeroI16 nz::i16!
NonZeroI32 nz::i32!
NonZeroI64 nz::i64!
NonZeroI128 nz::i128!
NonZeroIsize nz::isize!
NonZeroU8 nz::u8!
NonZeroU16 nz::u16!
NonZeroU32 nz::u32!
NonZeroU64 nz::u64!
NonZeroU128 nz::u128!
NonZeroUsize nz::usize!

Basic usage

use core::num::NonZeroU8;
// A NonZero type can be constructed by different
// types of arguments when using the matching macro
//
// such argument can be a numeric literal
const NZ_MIN: NonZeroU8 = nz::u8!(1);
let nz_two = nz::u8!(2);
// or a constant value
const NZ_MAX: NonZeroU8 = nz::u8!(u8::MAX);
let five = nz::u8!({ const FIVE: u8 = 5; FIVE });
// or even a constant expression
const RES: NonZeroU8 = nz::u8!({ 3 + 7 } - NZ_MIN.get());
// non-constant expression leads to compile-time error
// const OUTPUT: NonZeroU8 = nz::u8!({ 3 + 7 } - nz_two.get()); // casued by `mz_two.get()`
let result_as_nz = nz::u8!((NZ_MIN.get() & NZ_MAX.get()) + 7);

Limitations

const fn

Declarative macros (such as all the nz macros) cannot be used with constant function arguments since they are not currently recognized as constant values, as demonstrated in the code below.

use core::num::NonZeroU64;

const fn wrapping_add_nz(a: u64, b: NonZeroU64) -> NonZeroU64 {
    // `a` and `b` is not constant
    // the line below causes compile error
    nz::u64!(a.wrapping_add(b.get()))
}
let nz = wrapping_add_nz(2, nz::u64!(1));

const hygiene

When constants are used in a declarative macro, specifically in the most outer scope where a constant can be declared, there is a possibility of cyclic reference when an expression is expected as an argument and an outer constant is used within that expression. This "collision" can occur if any of the inner constants share the same identifier as the outer constant after the macro is expanded compile-time. The code snippet below demonstrates this scenario.

use core::num::NonZeroU16;

const NZ: NonZeroU16 = nz::u16!(0xA3FE);
const CHECK_ZERO: NonZeroU16 = nz::u16!(777);
// although `CHECK_ZERO` is used in `nz::u16!` macro, it will not result in
// an error when a constant with the same name is passed as part
// of a constant expression, because this inner macro constant is not
// declared in the most outer scope
const OK: NonZeroU16 = nz::u16!(CHECK_ZERO.get());
// using `NZ` is fine for the same reason
const ___NZ___INTERNAL___NUM___1___: u16
    = nz::u16!(NZ.get()).get();
// using `___NZ___INTERNAL___NUM___1___` constant as the argument
// causes compile-time error in the code line below, because the
// internal macro constant has the same identifier
const FAILS: NonZeroU16 = nz::u16!(
    ___NZ___INTERNAL___NUM___1___ // <-- error
);

This "collision" between the outer and inner constants leads to a compile-time error, specifically error [E0391], because the inner macro constant tries to use itself, creating a cyclic dependency during the evaluation of the macro at compile-time. Essentially, the code above has the same error as this single line:

const X: u8 = X;

License

This library is distributed under the terms of either of the following licenses at your option: