Files
Sprimo/skills/unsafe-checker/rules/ptr-05-no-const-to-mut.md
2026-02-12 22:58:33 +08:00

121 lines
2.8 KiB
Markdown

---
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
```rust
// 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
```rust
// 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`:
```rust
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:
1. `&T` means no mutation will occur
2. Multiple `&T` can exist simultaneously
3. Optimizations can be made based on these assumptions
When you mutate through cast pointer:
1. Other `&T` references see inconsistent values
2. Compiler may cache/eliminate reads
3. Results are unpredictable
## Checklist
- [ ] Am I trying to mutate through `&`?
- [ ] Should I use `&mut` instead?
- [ ] Should I use `Cell`, `RefCell`, or `UnsafeCell`?
- [ ] Is the original type designed for interior mutability?
## Related Rules
- `safety-08`: Mutable return from immutable parameter is wrong
- `safety-02`: Verify safety invariants