Initialization of a nonzero number, where assertion happens at compile time:
// Using macro_rules for performant compilation, tested to work in stable Rust
#[macro_export]
macro_rules! nonzero {
($n:expr) => {
// This is in a const context
const {
// Constant assertion that works even when running `cargo check`
const _: () = {
if $n == 0 { ::core::panic!("Cannot be zero!") }
};
// SAFETY: Checked that $n is not zero
unsafe { ::core::num::NonZero::new_unchecked($n) }
}
};
}
This macro accepts all numbers that can be wrapped inside a nonzero, and returns it wrapped in a nonzero struct, as long as that number is not zero. Else, an evaluation panic will happen at compile time.
This API allows following patterns:
const MY_NUM: i32 = 1234; // Some constant defined in an external crate
const MY_NUM_AS_NONZERO: core::num::NonZero<i32> = nonzero!(MY_NUM);
// This code panics at compile time
let zero = nonzero!(1u16 - 1);
// But this compiles
let non_zero = nonzero!(1u16 + 1);
Is this something that could be implemented into the standard library?
const MY_NUM_AS_NONZERO: NonZero<i32> = match NonZero::new(MY_NUM) {
Some(nz) => nz,
None => panic!("Cannot be zero!"),
};
Since this is totally safe there's less of a reason to do this in the stdlib.
On the other hand I think it might be more interesting to have dedicated literals for NonZero, or if we ever get some kind of custom literal or literal macros we could use those.
This is mostly an answer to how you can create a Vec using the vec! macro, but for NonZero numbers instead: By being short and concise. Though the vec! may only exist because it is not possible to do Vec::from([1, 2, 3, 4]) in a constant context.
It is mostly a way to act like a literal without being one: It is much easier to implement this macro to the standard library than to make an enhanced literal system.
And this macro depends on stable NonZero anyway, so if said literal system where to be stabilized, it will conflict with it the same way as the NonZero struct does.
Also this macro will never panic in a run-time context.
I don't think so. Instead, takes_nonzero(2) should just work, same as how literals are already type-overloaded so that takes_u8(2) and takes_i128(2) both work.
I wouldn't quite say it doesn't need a macro. For literals, occupying space in a larger call or construction exception, it's a lot of verbiage over a plain integer, and I'd rather have a macro for it until the plain integer works. Whether or not that macro needs to live in the stdlib is an additional question though.