id: ptr-05 original_id: G.UNS.PTR.02 level: G impact: CRITICAL clippy: cast_ref_to_mut
Do Not Manually Convert Immutable Pointer to Mutable
Summary
Never cast *const T to *mut T and dereference it to write. This violates aliasing rules and is undefined behavior.
Rationale
Creating *const T from &T implies immutability. Other references might exist. Writing through a *mut T created from *const T creates mutable aliasing, which is UB.
Bad Example
// DON'T: Cast *const to *mut
fn bad_mutate(value: &i32) {
let ptr = value as *const i32 as *mut i32;
unsafe { *ptr = 42; } // UB: Mutating through &
}
// DON'T: Use transmute to convert
fn bad_transmute(value: &i32) -> &mut i32 {
unsafe { std::mem::transmute(value) } // UB!
}
// DON'T: "I know this is the only reference"
fn bad_claim(value: &i32) {
// Even if you "know" there's only one reference,
// the compiler assumes & means no mutation
let ptr = value as *const i32 as *mut i32;
unsafe { *ptr += 1; } // Still UB - compiler may optimize incorrectly
}
Good Example
// DO: Take &mut if you need to mutate
fn good_mutate(value: &mut i32) {
*value = 42;
}
// DO: Use interior mutability
use std::cell::{Cell, RefCell, UnsafeCell};
struct Mutable {
value: Cell<i32>, // Interior mutability
}
impl Mutable {
fn modify(&self) {
self.value.set(42); // OK: Cell provides interior mutability
}
}
// DO: Use UnsafeCell if you need raw unsafe interior mutability
struct RawMutable {
value: UnsafeCell<i32>,
}
impl RawMutable {
fn modify(&self) {
// SAFETY: We ensure exclusive access through external means
unsafe { *self.value.get() = 42; }
}
}
The UnsafeCell Exception
UnsafeCell<T> is the ONLY valid way to get *mut T from &self:
use std::cell::UnsafeCell;
pub struct MyMutex<T> {
data: UnsafeCell<T>,
// ... lock state
}
impl<T> MyMutex<T> {
pub fn lock(&self) -> Guard<'_, T> {
// acquire lock...
// SAFETY: UnsafeCell allows this, lock ensures exclusivity
Guard { data: unsafe { &mut *self.data.get() } }
}
}
Why This Is Always UB
The compiler assumes:
&Tmeans no mutation will occur- Multiple
&Tcan exist simultaneously - Optimizations can be made based on these assumptions
When you mutate through cast pointer:
- Other
&Treferences see inconsistent values - Compiler may cache/eliminate reads
- Results are unpredictable
Checklist
-
Am I trying to mutate through
&? -
Should I use
&mutinstead? -
Should I use
Cell,RefCell, orUnsafeCell? - Is the original type designed for interior mutability?
Related Rules
safety-08: Mutable return from immutable parameter is wrongsafety-02: Verify safety invariants
你可能感兴趣的文章
Do Not Expose Raw Pointers in Public APIs
Unsafe Code Authors Must Verify Safety Invariants
Ensure Consistent Data Layout for Custom Types
Add Safety Section in Docs for Public Unsafe Functions
Do Not Use Union Variants Across Different Lifetimes
Use MaybeUninit
Ensure C-ABI Compatibility for Strings Between Rust and C
Do Not Dereference Pointers Cast to Misaligned Types
Use Dedicated Opaque Type Pointers Instead of c_void for C Opaque Types