Do Not Manually Convert Immutable Pointer to Mutable

2026-08-30 浏览 (1)


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:

  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

你可能感兴趣的文章

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 for Uninitialized Memory

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

Choose Appropriate Data Layout for Struct/Tuple/Enum

  • 所属分类: AI
  • 本文标签: rust
  • 版权声明: 本文链接 https://seaxiang.com/blog/oc6UTo9H