In Rust, creating a raw pointer is allowed in safe code (i.e. outside of unsafe
blocks), but then dereferencing it is unsafe:
let my_num_ptr: *const i32 = &my_num;
let value: i32 = unsafe {*my_num};
Undefined behaviour would only occur in a program which both creates and incorrectly dereferences a raw pointer. So long as one of these operations is only allowed in an unsafe
block, memory safety can be guaranteed for programs which have no unsafe
blocks.
What if, instead of requiring an unsafe
block for the dereference operation, it is instead required for the creation of the raw pointer? For example:
let i: const *u32 = unsafe {&4};
let b = *i;
Or even:
let i: mut *u32 = unsafe {&mut 8};
*i = 5;
In this hypothetical language, operations which create a raw pointer are unsafe (and must only occur in unsafe
blocks), but dereferencing them is allowed in safe code, on the basis that it's the programmer's responsibility to make sure when creating the raw pointer that the pointed-to value will stay alive at that memory address for long enough. Is this sound? Would there be any advantages to doing it this way instead of the Rust way?
Note that in Rust, a unsafe block does not mean that the entire program is unsafe, just that there are invariants the compiler can't check, so the programmer is responsible for them. If the programmer has made sure the rules are followed, it's perfectly safe.
In Rust currently, it is undefined behavior to dereference a null pointer, a unaligned pointer, or one that points to uninitialized memory. I'm suggesting that the undefined behavior happens when constructing such a pointer instead.