-
Notifications
You must be signed in to change notification settings - Fork 37
Description
A pointer-to-address cast is not symmetrical because the resulting pointer may not point to a valid object, may not point to an object of the right type, or may not be properly aligned. If a conversion in this direction is needed, std::mem::transmute will communicate the intent to perform an unsafe operation.
While the idea of using transmute to communicate the unsafety of the operation sounds good it's bad advice. as and transmute aren't equivalent when using them on integers and pointers. transmute doesn't use exposed provenance, while as does. This should recommend the exposed_provenance APIs. These aren't unsafe, but at least the resulting pointers are usable.
This is the compliant example. It does multiple questionable things. I added my comments with a + prefixed. Everything i am noting here is also noted by the explictly allowed lints here.
use std::convert::TryInto;
#[allow(dead_code)]
fn f2(x: u16, y: i32, _z: u64, w: u8) {
let _a: char = w.into();
let _b: Result <u32, _> = y.try_into(); // produce an error on range clip
let _c: i64 = x.into();
let d = f32::from(x); // u16 is within range, u32 is not
let _e = f64::from(d);
+ (this should probably be _e)
// let f = f32::from(e); // no From exists
// let g = ... // no From exists
let h: u32 = 0;
let p1: * const u32 = &h;
+ this exposes the provenance so the integer can be turned back into a pointer
let a1 = p1 as usize; // (compliant)
unsafe {
+ these don't expose provenance, so these integers can't be turned back into valid pointers.
+ if that was the wanted behaviour this should use the addr method.
let _a2: usize = std::mem::transmute(p1); // OK
let _a3: u64 = std::mem::transmute(p1); // OK, size is checked
// let a3: u16 = std::mem::transmute(p1); // invalid, different sizes
+ both of these pointers are invalid, because they have no provenance.
+ if as would have been used, they would have been valid
#[allow(integer_to_ptr_transmutes)]
let _p2: * const u32 = std::mem::transmute(a1); // OK
#[allow(integer_to_ptr_transmutes)]
let _p3: * const u32 = std::mem::transmute(a1); // OK
}
+ the from_bits to_bits functions do exactly that without requiring unsafe.
unsafe {
// does something entirely different,
// reinterpreting the bits of z as the IEEE bit pattern of a double
// precision object, rather than converting the integer value
#[allow(unnecessary_transmutes)]
let _f1: f64 = std::mem::transmute(_z);
}
}
fn main() {}
(by valid pointer i mean a pointer with provenance. having a pointer without provance of course isn't UB, but pretty useless)