Files
Sprimo/skills/unsafe-checker/rules/safety-07-unsafe-pair.md
2026-02-12 22:58:33 +08:00

2.9 KiB

id, original_id, level, impact
id original_id level impact
safety-07 P.UNS.SAS.07 P MEDIUM

Provide Unsafe Counterparts for Performance Alongside Safe Methods

Summary

When providing performance-critical operations that skip safety checks, offer both a safe checked version and an unsafe unchecked version.

Rationale

Users who need maximum performance can opt into unsafe, while others get safety by default. This follows the "safe by default, unsafe opt-in" principle.

Bad Example

// DON'T: Only provide unsafe version
impl<T> MySlice<T> {
    /// Gets an element by index.
    ///
    /// # Safety
    /// Index must be in bounds.
    pub unsafe fn get(&self, index: usize) -> &T {
        &*self.ptr.add(index)
    }
}

// DON'T: Only provide checked version when performance matters
impl<T> MySlice<T> {
    pub fn get(&self, index: usize) -> Option<&T> {
        if index < self.len {
            Some(unsafe { &*self.ptr.add(index) })
        } else {
            None
        }
    }
    // Missing: get_unchecked for performance-critical code
}

Good Example

// DO: Provide both versions
impl<T> MySlice<T> {
    /// Gets an element by index, returning `None` if out of bounds.
    #[inline]
    pub fn get(&self, index: usize) -> Option<&T> {
        if index < self.len {
            // SAFETY: We just verified index < len
            Some(unsafe { self.get_unchecked(index) })
        } else {
            None
        }
    }

    /// Gets an element by index without bounds checking.
    ///
    /// # Safety
    ///
    /// Calling this method with an out-of-bounds index is undefined behavior.
    #[inline]
    pub unsafe fn get_unchecked(&self, index: usize) -> &T {
        debug_assert!(index < self.len, "index out of bounds");
        &*self.ptr.add(index)
    }

    /// Gets an element, panicking if out of bounds.
    #[inline]
    pub fn get_or_panic(&self, index: usize) -> &T {
        assert!(index < self.len, "index {} out of bounds for len {}", index, self.len);
        // SAFETY: We just asserted index < len
        unsafe { self.get_unchecked(index) }
    }
}

Standard Library Patterns

Safe Method Unsafe Counterpart
slice.get(i) slice.get_unchecked(i)
str.chars().nth(i) str.get_unchecked(range)
vec.pop() vec.set_len() + ptr::read
String::from_utf8() String::from_utf8_unchecked()

Naming Conventions

  • Safe: method_name()
  • Unsafe: method_name_unchecked()
  • Or: get() vs get_unchecked()

Checklist

  • Does my safe method have an unsafe counterpart for hot paths?
  • Does my unsafe method have a safe alternative for normal use?
  • Are both methods documented with their trade-offs?
  • Does the unsafe version include debug assertions?
  • general-02: Don't blindly use unsafe for performance
  • safety-09: Add SAFETY comments