Files
Sprimo/skills/unsafe-checker/rules/safety-08-no-mut-from-immut.md
2026-02-12 22:58:33 +08:00

2.8 KiB

id, original_id, level, impact, clippy
id original_id level impact clippy
safety-08 P.UNS.SAS.08 P CRITICAL mut_from_ref

Mutable Return from Immutable Parameter is Wrong

Summary

A function taking &self or &T must not return &mut T to the same data without interior mutability.

Rationale

Returning &mut from & violates Rust's aliasing rules. The caller has an immutable borrow, so they can create additional & references. Returning &mut creates mutable aliasing, which is undefined behavior.

Bad Example

// DON'T: Return &mut from &self
struct Container {
    data: i32,
}

impl Container {
    // WRONG: This is undefined behavior!
    pub fn get_mut(&self) -> &mut i32 {
        unsafe {
            // Creating &mut from & is ALWAYS wrong
            &mut *(&self.data as *const i32 as *mut i32)
        }
    }
}

// DON'T: Transmute & to &mut
fn bad_transmute<T>(reference: &T) -> &mut T {
    unsafe { std::mem::transmute(reference) }  // UB!
}

Good Example

use std::cell::{Cell, RefCell, UnsafeCell};

// DO: Use interior mutability types
struct Container {
    data: Cell<i32>,          // For Copy types
    complex: RefCell<String>, // For non-Copy with runtime checks
}

impl Container {
    pub fn get(&self) -> i32 {
        self.data.get()
    }

    pub fn set(&self, value: i32) {
        self.data.set(value);
    }

    pub fn modify_complex(&self, f: impl FnOnce(&mut String)) {
        f(&mut self.complex.borrow_mut());
    }
}

// DO: Use UnsafeCell for custom interior mutability
struct MyMutex<T> {
    locked: std::sync::atomic::AtomicBool,
    data: UnsafeCell<T>,
}

impl<T> MyMutex<T> {
    pub fn lock(&self) -> MutexGuard<'_, T> {
        // Acquire lock...
        MutexGuard { mutex: self }
    }
}

struct MutexGuard<'a, T> {
    mutex: &'a MyMutex<T>,
}

impl<T> std::ops::DerefMut for MutexGuard<'_, T> {
    fn deref_mut(&mut self) -> &mut T {
        // SAFETY: We hold the lock, so exclusive access is guaranteed
        unsafe { &mut *self.mutex.data.get() }
    }
}

The Only Valid Pattern

The ONLY way to get &mut from & is through UnsafeCell:

use std::cell::UnsafeCell;

struct ValidInteriorMut {
    data: UnsafeCell<i32>,
}

impl ValidInteriorMut {
    // This is sound ONLY because UnsafeCell opts out of aliasing rules
    // AND we guarantee exclusive access (e.g., through a lock)
    pub fn get_mut(&self) -> &mut i32 {
        // Must ensure no other references exist!
        unsafe { &mut *self.data.get() }
    }
}

Checklist

  • Am I trying to return &mut from a & method?
  • If yes, am I using UnsafeCell or a type built on it?
  • Am I guaranteeing exclusive access before creating &mut?
  • Would Cell, RefCell, or Mutex solve my problem safely?
  • ptr-05: Don't manually convert *const to *mut
  • safety-02: Verify safety invariants