Pre-RFC: Allow custom values in core::num::NonZero

std::num::NonZero allows user to specify a type which cannot be zero,

NonZeroI8 in std::num - Rust (rust-lang.org)

so Option<std::num::NonZero> has the same size of std::num::NonZero

According to https://github.com/rust-lang/rust/pull/96947

rustc currently supports to specify a struct which cannot have the value of -1 (all bytes are 0xFF)

std::os::BorrowedFd and std::os::OwnedFd cannot be -1

However, there is no way to use them in libcore without libstd, and there is no way to specify such struct outside of libstd, because attributes rustc_layout_scalar_valid_range_start, rustc_layout_scalar_valid_range_end, and rustc_nonnull_optimization_guaranteed are unstable.

Since -1 is another widely used placeholder value besides zero, can we construct custom struct, which assumed not be -1?

I know I can store NonZero in my struct, and +1 or -1 in methods to simulate this, but it is a loss of performance.

I have the following proposals, from simple to hard

  1. Add structs NonNegativeOneI8, NonNegativeOneI32, NonNegativeOneU32, etc. to core::num, which specifies an integer that is known not to equal to -1 (all bytes are 0xff),

P.S.: Unsigned integer cannot be negative, but I do not find a better name than NonNegativeOne

  1. Add the following struct, which specify a custom value which is not allowed. Just adding a struct for -1 is not generic enough.
struct NonValueI8<const value: i8> {
}

Its public APIs are new_unchecked, new and get, which are defined similar to NonZero

  1. Add the following struct, which specify a custom range of value which is not allowed.
struct NonRangeI8<const begin: i8, const end: i8> {
}

a. If begin == end, there should be an error message

b. if begin < end, then the compiler can assume the value not in [begin, end)

c. if begin > end, then the compiler can assume the value not in [begin,MAX], and the value not in [MIN, end)

Its public APIs are new_unchecked, new and get, which are defined similar to NonZero

This effectively stabilizes rustc_layout_scalar_valid_range_start, rustc_layout_scalar_valid_range_end, and rustc_nonnull_optimization_guaranteed, and this is the most flexible solution. But this solution definitely requires much more testing in the compiler.

2 Likes

My understanding is that libs-api doesn't want more newtypes like this until the ergonomics of them can be better.

One direction towards making these nice that I'd like to see is having literals work for them (and existing things like NonZeroU32).

Of course, the other problem is that this is still only a partial step to the true Integer<MIN..=MAX> type, but that needs a whole bunch of const eval and particularly bounding stuff before it can go well.

2 Likes