Regardless I’d say managing to leak !Drop types or leaking after calling the destructor are 100% safe. The fact that most code that could leak is using non-copy types (or generic) suggests to me that one can take it as an axiom that “it is quite unlikely that safe code will leak”.
One example where it might be “nice” to be able to safely leak is an interaction with local custom allocators. So say you have a Tree<T, LocalAllocator>, where LocalAllocator is a type which has many instances (not a singleton like a global allocator) and your Tree is implemented with Boxes. Then if you can’t leak, each Box must contain a pointer to the allocator that allocated it. Because if your application panics, all the Boxes need to be free’d by the appropriate allocator (which is only known at runtime). This is however inefficient for the Tree. It uses a single allocator instance for all its nodes. We should be able to use this fact to avoid an entire extra pointer for every node.
Therefore we define a Mox<T, Allocator> type. Mox has the semantics of Box, but has to be manually free’d by passing it its allocator. However it still uniquely owns its data like Box. Given this design, we have two options for a Drop implementation:
// abort, we can't not leak memory (destructor bomb)
fn drop(&mut self) { abort!() }
// leak memory, call destructor
fn drop(&mut self) { ptr::read(self.ptr) }
Personally, I prefer the memory leak, because it means that your Tree doesn’t kill everything in a panic. Also, in the case of a local allocator, all the memory will probably be cleaned up very soon anyway. If there’s some kind of system that allows an object to ask for a global allocator instance, then this can potentially also free the memory if the Mox is handed a global allocator.