The implementation of binary heaps in std uses a Hole type. It stores a &mut [T], but one of the elements in this slice becomes temporarily invalid (it is repaired in the Drop implementation). Why is it okay to have a reference (as opposed to a pointer)? Does the existence of the reference imply the elements of the slice are valid? The code never reads the invalid element of the slice
the hole is not "invalid" per se, it is "duplicated" temporarily, via raw pointer read.
in rust, it is NOT unsound to create a "copy" of non-Copy type using ptr::read(), as long as only one of the two copies are being used, which is exactly what the Hole type guarantees. see the "ownership" section on ptr::read():
It's not the copy I have a problem with. It's the fact that there's a (mut!) reference to it. I believe that we are never reading the hole, it just seems weird to have a path to access it in this way. I would have guessed you'd need to convert the reference to a pointer (and separately hold the lifetime in a marker type).
Be careful with terminology here. In the https://www.ralfj.de/blog/2018/08/22/two-kinds-of-invariants.html meaning, what's under the &mut is not invalid.
Basically it's a &mut ManuallyDrop<T> -- notably it's not like a &mut MaybeUninit<T> -- but that doesn't actually matter because without ownership there's no material difference between T and ManuallyDrop<T>.
There are a few levels to consider.
For example, how exactly is "invalid" defined? I.e. is the referent really invalid? Its bytes were copied via pointer read (it wasn't moved as in let x = source, semantically making the source uninit). If it's invalid, it must be due to something more complicated, like how the read value gets used (or reading through a pointers in general would be untenable).
Perhaps it's "that place is invalid for typed copies once the new value is used in a typed fashion (until overwritten with something valid)"? (AFAICT, Hole doesn't do that.)
As another example, the pattern of temporarily move out of a &mut is considered sound,[1] so long as you restore the place before the &mut _ is used again. However, what's actually sound is probably going to be a bit looser than that -- I anticipate that "using" the &mut to coerce to a *mut to write back to the place will end up being formally considered sound at some point, for example. In which case the question becomes, what does count as a use of &mut that requires "referee validity enforcing"?
I did note that a Hole gets passed to a function in at least one place in the code, so if "passing across a function boundary" is a use that requires validity -- and the referee is truly invalid -- that could perhaps be considered problematic.
In a very general sense this is undecided, but if I understand everything correctly, things seem to be heading towards the answer being "no". (And here's an issue about "when" things have to be valid.) ((The aliasing model can still make the mere existence of a &mut unsound, etc.))
Honestly that's probably what I would do myself, too, until things are more formal and clear. unsafe Rust is still full of enough traps I prefer to be paranoid.
It's possible std is just not being as careful, or it's also possible for std to make more assumptions than ecosystem code due to being developed in-hand with rustc ("may technically be UB but we know its not exploited"). Or someone with more understanding over what's already been guaranteed to be sound considers it fine. I have no idea which it is
.
e.g. by Niko and Ralf, though I admit I'm not aware of any FCP on the topic âŠī¸
I wonder if the MaybeDangling semantic could extend to mark such "temporarily moved out" use case. to my understanding, its current intended use is for drop glues not requring dereferenceable.