If you have a C API taking a pointer-to-a-pointer, like void**, where the function fills in a pointer value, it's very easy to screw up if there is any pointer casting involved:
extern crate libc;
extern {
fn c_func(x: *mut *mut libc::c_void);
}
fn main() {
let x = 0 as *mut u8;
c_func(&mut (x as *mut libc::c_void));
println!("new pointer is {}", x);
}
This will always print new pointer is 0x0, no matter what c_func does.
Reason: the x as *mut ... cast is creating a temporary, that's disconnected from the original x and thus the modification happens to the anonymous stack slot that stores the result of the cast. The code should be written something like (&mut x) as *mut _ as *mut *mut libc::c_void.
This is really subtle to debug, so we could have a lint that assists in this case: "did you mean to take a reference to the result of a cast in this FFI call" (could have it apply to non-FFI things too, and presumably it should only apply when there are &mut pointers involved).
If you have a C API taking a pointer-to-a-pointer, like
void**, where the function fills in a pointer value, it's very easy to screw up if there is any pointer casting involved:This will always print
new pointer is 0x0, no matter whatc_funcdoes.Reason: the
x as *mut ...cast is creating a temporary, that's disconnected from the originalxand thus the modification happens to the anonymous stack slot that stores the result of the cast. The code should be written something like(&mut x) as *mut _ as *mut *mut libc::c_void.This is really subtle to debug, so we could have a lint that assists in this case: "did you mean to take a reference to the result of a cast in this FFI call" (could have it apply to non-FFI things too, and presumably it should only apply when there are
&mutpointers involved).