Ah, right, thanks.
These functions are not special in this sense, any loads and stores should have this property. ptr::{read,write} are at most special in that they are the primitive way to express “write without dropping old contents first” (in contrast to *p = x; which drops) and “read without concern for move semantics” (in contrast to let x = *p; which only works for Copy types). But this isn’t really true either, at least of ptr::read (check out its implementation, you could write that in stable Rust today).
Aside from that, the IRC discussion @gnzlbg alludes to was about raw pointer loads and stores. When you add references in the mix, it gets a lot more complicated. While it’s probably fine to cast a *const T to a *const [u8; size_of::<T>()] and copy around any of those bytes even if they’re padding or uninitialized, a &[u8] to the same memory is a rather different story. References have quite strong invariants both about their address (+ metadata, if any) and about the concents of the memory they point at – the main open questions are about when exactly these invariants are asserted (e.g., “all the time”, “at function call boundaries”, “when you load or store through the reference”, etc.).
To say that a &[u8] to padding or unintialized memory is fine amounts to saying there is no such thing as uninitialized or padding memory, every byte of memory always one of 256 possible values and it’s safe to do anything with any of these bytes. I don’t want to rehash that debate here but this option is very radical and, I believe, unacceptable for Rust because of how much it constrains the optimizer.
Thus, I don’t think a &T -> &[u8] conversion could be safe for all T even if there was then no way to reinterpret the &[u8] as any other type (e.g. imagine it was a trait object for trait Blob { fn get_byte(idx: usize) -> u8; }). Or, put differently, there needs to be some way to correctly handle padding and uninitalized memory in unsafe code, but I don’t see any (desirable) way to give safe code such capabilities.