Why is std::num::NonZeroUsize::new_unchecked unsafe?

Hi!

Why is the NonZeroUsize::new_unchecked method marked as unsafe? If I understand correctly, the unsafe keyword is reserved for methods/functions that can lead directly or indirectly to undefined behavior (UB).

But I fail to see how the unsafe status of NonZeroUsize::new_unchecked is relevant since all its methods are safe, and, as such, are not guaranteed to actually return a non-zero value. In other words, unsafe code cannot trust the NonZeroUsize::get method (and should perform a non-zero test) because it is not itself unsafe (as explained in the nomicon).

1 Like

The purpose of NonZeroUsize is to give a promise to the compiler that the value inside of it is never 0. That means that e.g. Option<NonZeroUsize> is the same size as usize with None being represented as 0. If that constructor were safe, you could violate that guarantee.

2 Likes

Unsafe code can trust this method to return non-zero, precisely because there is no safe way to construct a NonZeroUsize without a non-zero value.

This is unlike the examples in the nomicon, like "can BTreeMap trust Ord implementations to be correct?" In that case, an incorrect Ord implementation can be written in safe Rust, so we can't allow such code to violate memory safety.

2 Likes

Thanks! Looks like a good reason for this to be unsafe.

The fact there is no safe way to construct NonZeroUsize without a non-zero value does not mean ::get will actually return a non-zero value. If unsafe code cannot trust that safe code is correct, then it cannot trust the fact a bogus implementation of :get won't return 0. An example of bogus implementation would be:

fn get(self) -> usize {
    0
}

There's an important nuance that that's missing. Unsafe code cannot trust that arbitrary safe code is correct. It can absolutely trust that safe code in its own module is correct. (Otherwise wrapping unsafe things in safe abstractions could never work, since doing so inherently involves some safe code.)

3 Likes

Unsafe code can also assume that the standard library APIs are not actively malicious.

For example, unsafe code can and does rely on the fact that [T]::as_ptr actually returns a pointer to the start of slice's allocation.

2 Likes

Thanks @sfackler, @scottmcm, that clarifies things a lot!