Detect whether a type has an all zeros niche

Is it possible to detect in a const whether a type T has a niche in the all zeroes bitpattern?

Something like size_of<T>() == size_of<Option<T>>() comes close, but that only detects if the type has a niche, not if that niche is the all zeroes bitpattern (e.g. it would not work for OwnedFd).

Another approach would be to try to conjure a None::<T>, and read whether the bytes are all zero (playground), but that also fails since there's no way to avoid reading the uninitialized padding bytes.

In particular, I would like to be able to write something like the following contrived example:

fn create<T>() -> Option<T> {
        // SAFETY:
        // - The type has a niche in the all zeroes bitpattern.
        // - `Option<T>` is guaranteed to use that niche.
        unsafe { std::mem::zeroed() }
    } else {

I see that std::intrinsics::assert_zero_valid exists, a similar is_zero_valid would fulfil my requirements.

That's incorrect. The guarantee only holds for an explicit set of types and transparent wrappers around them. Other types may have a niche but not have the optimization applied, or have a niche including all-zeros plus other values and have the optimization apply using a non-all-zero niche value.

It's unlikely to ever be guaranteed at the generic level.

1 Like

An opt-in trait for the property does exist in bytemuck. Technically you'd have to check if the zeroed version is None or not (maybe some day there will be a NonMaxU32 where Option<NonMaxU32> is guaranteed to use the single (but not zeros) niche value optimization).


That's incorrect

True, I only used that guarantee in this simplified example anyhow.

iirc there already is a type with a single non-zero niche: BorrowedFd in std::os::fd - Rust

icr if it's guaranteed yet, but I expect Option<BorrowedFd> to be guaranteed to represent None as -1.

It's only guaranteed for the types listed in the linked representation section.

1 Like

Can you elaborate on a less-contrived example? Can you say more about how you're going to use the result of the check?

1 Like

I wanted to use the information to, as an optimization, store things in an MaybeUninit, and then at Drop, if the bytes were all zeroes, we'd be able to skip dropping the inner type.

But as I tried to write down an example here, I found out that that actually also runs into issues, again because you can't compare uninitialized padding bytes, not even to compare them against zero.

So yeah, I don't think what I'm trying to do is possible :confused:

Which is, of course, plan which could never work: when, more than half-century ago, “uninitialized memory” thingie was added to C and other languages the idea was to save few cycles and few bytes which were needed to zero-initialize region of memory.

Thus your plan was doomed from the very beginning on a deeply conceptual level: the optimization you planned to implement manually clashes, badly, with the optimizations for MaybeUninit which were there for half-century!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.