3.0 KiB
3.0 KiB
id, original_id, level, impact, clippy
| id | original_id | level | impact | clippy |
|---|---|---|---|---|
| ptr-06 | G.UNS.PTR.03 | G | LOW | ptr_as_ptr |
Prefer pointer::cast Over as for Pointer Casting
Summary
Use the cast() method instead of as for pointer type conversions. It's clearer and prevents accidental provenance loss.
Rationale
cast()only changes the pointed-to type, not pointer propertiesascan accidentally convert to integer and back, losing provenancecast()is more explicit about intent- Better tooling support (clippy, miri)
Bad Example
// DON'T: Use `as` for pointer casts
fn bad_cast(ptr: *const u8) -> *const i32 {
ptr as *const i32 // Works, but less clear
}
// DON'T: Accidental provenance loss
fn bad_roundtrip(ptr: *const u8) -> *const u8 {
let addr = ptr as usize; // Converts to integer
addr as *const u8 // Loses provenance information!
}
// DON'T: Multiple `as` casts in chain
fn bad_chain(ptr: *const u8) -> *mut i32 {
ptr as *mut u8 as *mut i32 // Hard to follow
}
Good Example
// DO: Use cast() for pointer type changes
fn good_cast(ptr: *const u8) -> *const i32 {
ptr.cast::<i32>()
}
// DO: Use cast_mut() for const-to-mut (when valid)
fn good_cast_mut(ptr: *const u8) -> *mut u8 {
ptr.cast_mut() // Only use when mutation is valid!
}
// DO: Use cast_const() for mut-to-const
fn good_cast_const(ptr: *mut u8) -> *const u8 {
ptr.cast_const()
}
// DO: Chain casts clearly
fn good_chain(ptr: *const u8) -> *mut i32 {
ptr.cast_mut().cast::<i32>()
}
// DO: Use with_addr() for address manipulation (nightly)
#[cfg(feature = "strict_provenance")]
fn good_provenance(ptr: *const u8, new_addr: usize) -> *const u8 {
ptr.with_addr(new_addr) // Preserves provenance
}
Pointer Method Reference
| Method | From | To | Notes |
|---|---|---|---|
.cast::<U>() |
*T |
*U |
Changes pointee type |
.cast_mut() |
*const T |
*mut T |
Removes const |
.cast_const() |
*mut T |
*const T |
Adds const |
.addr() |
*T |
usize |
Gets address (nightly) |
.with_addr(usize) |
*T |
*T |
Changes address, keeps provenance |
.map_addr(fn) |
*T |
*T |
Transforms address |
Provenance Considerations
// Provenance = permission to access memory
// BAD: Loses provenance
let ptr: *const u8 = &data as *const u8;
let addr = ptr as usize;
let ptr2 = addr as *const u8; // ptr2 has no provenance!
// GOOD: Preserves provenance (nightly strict_provenance)
let ptr2 = ptr.with_addr(addr); // Still has permission
// GOOD: Use expose/from_exposed when provenance must cross integer
let addr = ptr.expose_addr(); // "Expose" the provenance
let ptr2 = std::ptr::from_exposed_addr(addr); // Recover it
Checklist
- Am I using
aswherecast()would be clearer? - Am I accidentally converting through
usize? - Do I need to preserve provenance?
Related Rules
ptr-04: Alignment considerations when castingptr-05: Don't convert const to mut improperly