Printing data behind `&raw str`?

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.

I see; I assumed this 'd be done within unsafe {..} but that was a mistake.

So it' be:

    unsafe {
        let s2 = &*r1; // r1 is the *mut str
        println!("{:?}", s2);
    }

For example (which may crash or print random stuff).

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.)

Yes, that was the aim, by tweaking the value of .repeat(n) until the pointer changed address.

Oh, that makes sense now!

In general, unsafe does not change what code does. It only allows you to use new operations that previously would not have compiled.