That said, given that unwinding does exist, it is the responsibility of unsafe code blocks to account for it. They must not allow safe code to create UB. That they mostly don't account for it is a culture issue.
It is a pretty fundamental feature of the current version of rust (search term: NLL) that borrows do not end upon the last reference (or any type with lifetime parameter, but I'll just say "reference") with that lifetime being dropped; rather, they end when the last reference will no longer be used. This is crucial to ergonomics; otherwise you could not have:
let x = vec![];
let y = &mut x;
y.push(0);
x.push(1);
because, strictly speaking, y is dropped at the end of the block.Dropping a reference quite often doesn't even count as a use (#[may_dangle] types, and actual references). But even if it does, if the drop for any reason doesn't happen, then it's irrelevant for determining when the "last use" of a lifetime will be. It is also another fundamental feature of the current version of rust that leaks are safe and sound and they cannot be considered unsafe/unsound without removing useful features (this was not an intentional design decision originally but see search term "leakpocalypse").
Combined, this means that just because the lifetime of a value typed `Guard<'a>` ended, doesn't mean that value actually got dropped; it just went away for some unspecified reason. This doesn't mean drops are unreliable or anything like that; even though leaks are not unsound, in "correct" rust code you pretty much have to intentionally leak for it to happen. But it does mean that if some code must run to prevent data races, you can't just put it in a Drop impl and trust it will run.
Contrast mutex guards, which unlock the mutex upon dropping. This is still perfectly sound in the face of leaks, because it fails open: if drop doesn't run, mutex stays locked, so no data races can happen.