Feature request: References to empty objects

It is safe. (assuming the ZSTs existense isn't used to prove an invariant)

Pointer/reference to a ZST cannot be dangling. They also can't alias so having multiple mutable references to a ZST is safe even if they have the same address. The only requirement is that the reference is nonnull and aligned.

For example when dealing with ZSTs Vec<T> doesn't allocate any memory, it just creates the values out of thin air as needed.

let mut v = Vec::new();

v.push(()); // this doens't allocate memory
v.push(());

// this is possible in safe rust even tough a and b are mutable references
// and point to the same memory location because references to ZSTs cannot alias
let [a, b] = &mut *v else { panic!() }; 

Here is a good explanation about ZSTs and how they interact with pointers.

2 Likes

You best bet would be waiting for feature(generic_const_exprs) that allows you create a MaybeZstRef<'a, T> wrapper that is ZST when T is ZST.

I've solved my problem of zero-size reference this way (by creating a special reference type for my needs):

/// Its advantage over `&CanisterStableMemory` is that `CanisterStableMemoryRef` is zero-size.
struct CanisterStableMemoryRef;

impl Deref for CanisterStableMemoryRef {
    type Target = CanisterStableMemory;

    fn deref(&self) -> &Self::Target {
        &CanisterStableMemory {}
    }
}
1 Like

EDIT: I misunderstood the example, I agree that it shows why the proposal is unsound.

Are you allowed to do this in generic code? I think it is unsound for code which uses the existence of a ZST to prove something.

As far as I can see, the main issue with the case you present is &mut &ZST, where &ZST is annotated with #[allow_zero_sized] in the struct where it's defined. Except for that case, I think that unsafe code which relies on the address being stable will always know about the annotation, and will thus know not to rely on it.

I'm not sure what you mean, what shouldn't be allowed in generic code?

The example has no &mut &ZST though.

I'm not sure about that. If unsafe code handles out such a reference to safe code then it won't know how it will be used, including being put in a #[allow_zero_sized] field.

Generic code is not allowed to generate a reference to a ZST out of thin air.

What I meant was that the only way unsafe code can be exposed to an annotated reference to a ZST, without knowing it's annoteted, is through a mutable reference. If the unsafe code knows about the annotation, then it's the unsafe code's responsibility to take it into account.

It depends. It is allowed to if it owns a ZST but didn't store it somewhere in particular. But in this case you are given a reference to a ZST and #[allow_zero_sized] somehow has to recreate the same exact reference, which is not possible. Thus the only thing it can do is generate a reference out of thin air and that's unsound.

That's not true. In the example above a plain reference to a ZST is returned by unsafe code, and then it is exposed when that reference is used to call a method. No mutable reference is created ever.

In the example above the unsafe code doesn't know about the annotation though.

It's in the same module, so it does. The unsafe code relies on a property of the field, which it knows might not be true.

You can put EvenAddr in its own module/crate that knows nothing about #![allow_zero_sized] and nothing would change.

1 Like

You're right - I missed that. Sorry for dragging this on :sweat_smile:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.