Search code examples
rustreferenceunsafe

Why is casting `&mut &mut i32` as `*mut *mut i32` invalid


I can cast &mut i32 to *mut i32 in rust like so

fn main() {
    let mut x = 1;
    let mut xref = &mut x;
    unsafe {
        let xref_ptr = xref as *mut i32;
        *xref_ptr = 2;
    }
    println!("{}", x);
}

Prints 2.

But I can't cast &mut &mut i32 to *mut *mut i32 and I don't understand why.

fn main() {
    let mut x = 1;
    let mut xref = &mut x;
    let mut xrefref = &mut xref;
    unsafe {
        let xrefptr = xrefref as *mut (*mut i32);
        **xrefptr = 2;
    }
    println!("{}", x);
}
error[E0606]: casting `&mut &mut i32` as `*mut *mut i32` is invalid
  --> src/main.rs:16:23
   |
16 |         let xrefptr = xrefref as *mut (*mut i32);
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^

If references are pointers under the hood I was expecting &mut &mut i32 to be a pointer to pointer to an i32 but it appears this cast is wrong.

Why is the first cast allowed but not the second?


Solution

  • As @PitaJ points out, you're generally only allowed to do one cast at a time and casts aren't transitive. I.e. T as U being valid and U as V being valid doesn't imply that T as V is valid.

    But you can just do the cast in two steps (in safe code!):

    let mut x = 1;
    let mut xref = &mut x;
    let mut xrefref = &mut xref;
    let xrefptr = (xrefref as *mut &mut i32) as *mut *mut i32;
    unsafe {
        **xrefptr = 2;
    }
    println!("{}", x); // 2