I’m not sure that what is needed is design by contract, but I definitely think that it is the logical conclusion of trying to push unsafe to the limits. We should implement whatever is in the right nexus of safety, power, and usability, and it’s far from clear to me that a “menu of contracts” is such a thing.
While some unsafe operations would lend themselves well to debug-mode checks for validity, others would not. Never dereferencing a null pointer, for instance, is such a simple precondition to check that the compiler can even insert that without the user having to do so.
Unfortunately, manually verifying the memory model is nowhere near so easy: we would need a complex sanitizer-level instrumentation, for instance, to ensure that, when casting *mut T to &mut T, the pointer is not aliased by any other safe reference anywhere in the program. This is certainly far beyond anything an author could, in general, write into a debug assertion.
FFI is even worse. FFIs involve calling code about which Rust has absolutely no information. It could always be buggy and corrupt memory in some way that isn’t visible even to the developer calling it. So there will generally be no way for the caller to ensure that, in context, the call is safe.
Encouraging authors of unsafe code to use debug assertions about invariants that they expect within their own code is probably a fine idea, though.