For unsafe methods, the safety requirements can be divided into exact two categories: requirements before invoking (Vec::set_len requires new elements are initialized before this function), and requirements after invoking (Pin::new_unchecked requires no move after, ManuallyDrop::drop requires no use after). Violating the requirement is immediate UB, even if the requirement is repaired in a short time. The std documentation has a great example for this.
However, for unsafe fields, things become complex. Let's take the example in the lang meeting of unsafe fields:
struct CacheArcCount<T> {
// SAFETY: This `Arc`'s `ref_count` must equal the
// value of the `ref_count` field.
unsafe arc: Arc<T>,
// SAFETY: See [`CacheArcCount::arc`].
unsafe ref_count: usize,
}
Then how can we write a correct ref_increment?
fn ref_increment(&mut self) -> Arc<T> {
let new_value = unsafe { Arc::clone(&self.arc) };
unsafe { self.ref_count += 1; }
new_value
}
(Let's assume that there is some drop guard to make sure self.ref_count is decremented if the new value is dropped. That is off topic)
This code is obviously correct in current Rust without unsafe fields. And considering unsafe fields, the safety requirement of self.arc and self.ref_count is always held before and after self.ref_increment, but not inside it.
After the new_value is created, and before incrementing self.ref_count, the contract that "This Arc's ref_count must equal the value of the ref_count field" is not held. Is this code unsound in terms of unsafe fields?
This can actually lead to safety problem if:
fn ref_increment2(&mut self) -> Arc<T> {
let new_value = unsafe { Arc::clone(&self.arc) };
some_func_may_panic();
unsafe { self.ref_count += 1; }
new_value
}
Then, here we go, an exception safety violation!
As a result, the core problem of this post is that:
How to define the moment that unsafe fields' contracts need to be held?
For an intuitive point of view, where the answer is "anytime", then there could be no sound version of ref_increment.