--- id: ptr-06 original_id: G.UNS.PTR.03 level: G impact: LOW clippy: 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 properties - `as` can accidentally convert to integer and back, losing provenance - `cast()` is more explicit about intent - Better tooling support (clippy, miri) ## Bad Example ```rust // 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 ```rust // DO: Use cast() for pointer type changes fn good_cast(ptr: *const u8) -> *const i32 { ptr.cast::() } // 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::() } // 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::()` | `*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 ```rust // 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 `as` where `cast()` would be clearer? - [ ] Am I accidentally converting through `usize`? - [ ] Do I need to preserve provenance? ## Related Rules - `ptr-04`: Alignment considerations when casting - `ptr-05`: Don't convert const to mut improperly