Can *mut u8 being invariant be observed?

Assuming I have a struct Orange(*mut u8) the *mut makes it invariant with respect to u8, is there any way this type would behave differently than struct Apple(*const u8).

No, there must be a lifetime for you to observe it.

Looking for an answer I dug up this 0738-variance - The Rust RFC Book here it says:

However, in practice, *mut pointers are often used to build safe abstractions, the APIs of which do not in fact permit aliased mutation. Examples are Vec , Rc , HashMap , and so forth. In all of these cases, the correct variance is covariant -- but because of the conservative treatment of *mut , all of these types are being inferred to an invariant result.

What do they mean, where would lifetimes for these cases come from?

Well for example a Vec<&'a str> would have a lifetime on the raw pointer.

Using *mut u8 inside Vec would then mean we can't widen or narrow the 'a even though that would be safe?

Well, it would be an *mut &'a str in that case, but yes. Using a mutable raw pointer would disallow narrowing the lifetime.

1 Like

If you had a *mut &'a str, the &'a str would be invariant so the 'a couldn't change.

u8 is also invariant behind a *mut, but it has no lifetime, so it doesn't matter -- you cant' observe it.

Note that the RFC is from 2014. Vec is implemented with Unique now. That part of the RFC was basically "if we adapt this model, how do we best soundly adjust current practices?"

1 Like

Sure, but Unique is defined like this:

pub struct Unique<T: ?Sized> {
    pointer: *const T,
    // NOTE: this marker has no consequences for variance, but is necessary
    // for dropck to understand that we logically own a `T`.
    // For details, see:
    _marker: PhantomData<T>,

The variance rules are real and in use in Rust today.

1 Like

Right, from the RFC:

The complete solution to this seems to have two parts. First, for convenience and abstraction, we should not be building safe abstractions on raw *mut pointers anyway. We should have several convenient newtypes in the standard library, like ptr::Unique , that can be used, which would also help for handling OIBIT conditions and NonZero optimizations. In my branch I have used the existing (but unstable) type ptr::Unique for the primary role, which is kind of an "unsafe box". Unique should ensure that it is covariant with respect to its argument.

However, this raises the question of how to implement Unique under the hood, and what to do with *mut T in general. There are various options:


  1. Rewrite safe abstractions to use *const (or even usize ) instead of *mut , casting to *mut only they have a &mut self method. This is probably the most conservative option.

Variance is a property of "type constructors". It tells us something about: "How does Type<T> react when T changes?". I.e variance is something we bring up when talking about that Type<_> as a general thing.

Since we don't have any type constructor here, no type (or lifetime) parameters, the invariance concept doesn't apply at all :slight_smile:.