This is true of the safety invariant, yes. If I have a str
which is valid UTF-8, I can do anything that the API offers. If I have a str
which is invalid UTF-8, though, which does not satisfy the safety invariant but does the validity invariant, I can't do many otherwise safe operations; for example, s.chars().last()
could index out of bounds, causing UB.
It is the safety invariant that you can dereference &T
and get a valid T
. It is a validity invariant that you can dereference &T
and get size_of::<T>()
bytes, and that the reference is aligned to align_of::<T>()
bytes.
Of course, this is non normative, but this is the general consensus of the unsafe code working group, and making validity recursive is really hard to check for basically no optimization benefit.
Making a bool
which is not 0x00
or 0x01
is a different class of invalid, because that's a validity invariant of the type. Moreover, it's the "bitstring validity," which is used for layout optimizations.
&!
could still potentially have not (unsafe but) valid values still, if we make it part of the validity invariant that the pointee type is inhabited. But as currently informally defined, transmuting &()
to &!
gives you a valid but unsafe instance of &!
where not fulfilling the safety invariant of a reference means that the only sound operations are
- a typed copy,
- turn it into a pointer, or
- dereference to
size_of::<T>()
bytes (but to not do a typed copy of said bytes, or otherwise use them at type T
).
This ignores any retagging operations done by Stacked Borrows or any other memory model enforcing the borrow quality of references, as this does care about the type of the pointee (though similarly doesn't necessarily recurse into retagging the pointee's contained references).
We might end up renaming safety/validity invariants in the future, because informally "validity" can refer to an instance which satisfies the safety invariants. But those are the terms we currently have.
(And so is the null pointer.)
Note that it's only to dereference at type !
. MaybeUninit<!>
is fine, as is [u8: 0]
or any other inhabited ZST. The UB from dereferencing a pointer to !
comes to that doing a typed copy of an uninhabited type with the validity invariant of false.