An example of this, is aliasing mutable references. Rust has been designed to assume that two mutable refs will never alias, if you attempt to do this it is instant UB. My understanding is that even creating the aliasing reference is UB, even if you don't use it.
Another example would be uninitialized values. In rust, the compiler assumes that values are always "valid" and initialized. Since you need to allocate memory before writing to it, you need some way to safely have uninit values, and this is what the MaybeUninit wrapper type is for. The wrapper allows you to safely have uninit values, and once you write to them you can tell the compiler that they are initialized, but if you tell the compiler before on accident, it is UB.
References also are guaranteed to point to initialized and valid data, and can never be null, though my understanding is that there is some uncertainty about the exact rules of this with regards to uninitialized values and the exact semantics may change in the future.
(There are also a lot more things that I don't know very much about!)
All of these things are assumed to never happen, and optimizations are performed based on that assumption.
The nice part about rust, is that it makes it impossible to represent invalid states using the type system!
For aliasing, you can only have a single live mutable reference at any time, attempting to create a second one while another is live is a compile time error!
For uninitialized values, you simply can't create uninitialized values at all in safe rust. My understanding is that the only way to create an uninitialized value is with the MaybeUninit type or by using raw pointers.
So rust still has heaps of UB, but it doesn't allow you to do it by default, so you still get some of the optimizations you'd expect.
I think there are some cases where rust is missing out on optimizations though, like with signed int overflow for example, and probably more that I don't know about!