In my efforts to understand what's going on, I found this page in the Rustonomicon which says that the address of any u32 must be a multiple of 4 bytes. However, the way I'm calling this code above basically guarantees that this is violated. I feel like I should be using std::ptr::read_unaligned, but I'm not sure if I need that since it works already. Also, running cargo miri test reports no errors.
My two questions are:
Is this safe?
If so, why would I ever use std::ptr::read_unaligned()?
Miri reports UB on your code. You need to use cargo miri run to run the main function. cargo miri test runs unit tests, which your code sample doesn't show any of so it may not have run anything at all.
You can see the Miri failure on this playground by going to Tools > Miri
error: Undefined Behavior: accessing memory with alignment 1, but alignment 4 is required
--> src/main.rs:18:18
|
18 | unsafe { std::ptr::read(data) }
| ^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment 1, but alignment 4 is required
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `Stack::get::<u32>` at src/main.rs:18:18: 18:38
note: inside `main`
--> src/main.rs:27:22
|
27 | println!("{:?}", stack.get::<u32>(2)); //1
| ^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error
Undefined Behavior may include working in the way you would think if it were defined. But since it's undefined, that can change at any time, even possibly between different instances at runtime.
So I just have to follow the same rules I would with std::mem::transmute, right? I'm really only considering using this for the primitive numbers like u8-u128, which should be safe since every combination of bits is valid for those types, right?
Well, after all, what you are doing is basically a very roundabout transmute_copy().
But there's no reason to use unsafe for this purpose. This isn't big enough of a task to pull in bytemuck either, IMO. For primitives, bit-packing is trivial enough to roll your own (assumes little-endian but easy to modify to BE):