Perhaps a more useful phrasing would be, it's UB to inspect uninitialized memory? But then again perhaps I'm just moving the nuance around to the definition of "inspect".
You can copy around a MaybeUninit<u8>, but if the memory is actually uninitialized, reading the value into a u8 is still UB. There is no sound way to know what the bits of some uninitialized memory are. If you're thinking "below the language level", the notional action of copying around an uninitialized MaybeUninit<u8> is also allowed to change the bits actually in memory.
The gotcha is that what's UB in this case is "The following values are invalid (at their respective type): … An integer (i*/u*), floating point value (f*), or raw pointer obtained from uninitialized memory, or uninitialized memory in a str.".
MaybeUninit is one of the few (only?) Rust types that can safely handle uninitialized memory. And the deep issue is that someone who's worked in other languages (like assembly) may be thinking in terms of bit patterns, and the underlying reason you can't produce a value from uninitialized memory is that as well as all possible bit patterns, there's the possibility that it's "poison" or "undef" in terms of LLVM semantics.
Depends how you want to think about it. MaybeUninit is just a union, so everyone can create their own union that can also safely handle uninitialized memory in the same way as MaybeUninit. But yes, if you look in the standard library, it's basically the only thing to expose the uninitialized memory safely -- critically, even ManuallyDrop requires valid bytes always.
(Option can also be thought of a "safe MaybeUninit" if you want, because None is carrying around uninitialized memory. But I don't think that's how anyone ever really thinks about it, in my experience.)