These are just a few snippets written to play with the basics of unsafe.
2 unique refs
fn main() {
let mut s = 5;
let r1 = &raw mut s;
let r2 = &raw mut s;
unsafe {
println!("{:?} {:?}", r1, r2);
println!("{:?} {:?}", *r1, *r2);
}
}
Read random memory
fn main() {
let r1 = 0x10 as *const i32;
unsafe {
println!("{:?}", *r1);
}
}
Print raw string
Even though &str, &mut str can be printed, *mut str can not, and the data structure is printed (the fat pointer).
Why was it done to print the pointer, rather than the underlying data? Can the data be printed somehow (so that it crashes here)? Or why isn't it possible?
fn main() {
let mut s = "hello".to_string();
println!("Pointer: {:p}, Len: {}, Capacity: {}",
s.as_ptr(), s.len(), s.capacity());
let r1 = &raw mut s[..];
let r2 = &raw mut s[..];
s.push_str(&" world".repeat(90));
println!("Pointer: {:p}, Len: {}, Capacity: {}",
s.as_ptr(), s.len(), s.capacity());
unsafe {
println!("{:?} {:?}", r1, r2);
// But how can I try to print the data they point to?
}
}
because raw pointers are not always pointing to valid data, thus requires unsafe to dereference, in other words, the programmer is responsible for the validity of raw pointers, the type itself does not guarantee it. that's one of the key difference between raw pointers and references.
Because it's unsafe to dereference raw pointers -- it might cause UB -- but the Display and Debug (etc) trait methods are safe to call (don't require unsafe). Being able to cause UB without using unsafe is Rust's #1 no-no.
That's UB for example, so there's no guarantee it will do anything sensible or intuitive. Even if you manage to avoid a segfault, it may not actually read the memory at the given address, for instance.
Like so...
unsafe {
println!("{:?} {:?}", &*r1, &*r2);
}
...but as the rest of the code is, that's UB, due to aliasing violations for one. (Additionally probably a UAF because the push_str will likely reallocate and your original pointers will be pointed at deallocated (or reallocated) memory.)