Use References Instead of Raw Pointers When Calling Safe C Functions

2026-08-30 浏览 (1)


id: ffi-09 original_id: P.UNS.FFI.09 level: P impact: MEDIUM

Use References Instead of Raw Pointers When Calling Safe C Functions

Summary

When wrapping C functions that don't need null pointers, use Rust references in the safe wrapper to enforce non-null at compile time.

Rationale

  • References guarantee non-null
  • References have lifetime tracking
  • Raw pointers should stay in the unsafe FFI layer
  • Safe Rust API should use safe types

Bad Example

extern "C" {
    fn c_process(data: *const u8, len: usize);
}

// DON'T: Expose raw pointers in safe API
pub fn process(data: *const u8, len: usize) {
    // Caller might pass null!
    unsafe { c_process(data, len); }
}

// DON'T: Unsafe function when it could be safe
pub unsafe fn process_unsafe(data: *const u8, len: usize) {
    // Why force caller to use unsafe?
    c_process(data, len);
}

Good Example

extern "C" {
    fn c_process(data: *const u8, len: usize);
    fn c_modify(data: *mut Data);
    fn c_optional(data: *const Data);  // Can be null
}

// DO: Use slice reference for safe API
pub fn process(data: &[u8]) {
    // Reference guarantees non-null
    // Slice guarantees valid length
    unsafe { c_process(data.as_ptr(), data.len()); }
}

// DO: Use &mut for exclusive access
pub fn modify(data: &mut Data) {
    // Mutable reference guarantees:
    // - Non-null
    // - Exclusive access
    // - Valid for duration
    unsafe { c_modify(data as *mut Data); }
}

// DO: Use Option<&T> for nullable parameters
pub fn optional(data: Option<&Data>) {
    let ptr = data.map(|d| d as *const Data).unwrap_or(std::ptr::null());
    unsafe { c_optional(ptr); }
}

// DO: Wrap FFI types in safe Rust types
pub struct SafeHandle(*mut c_void);

impl SafeHandle {
    pub fn new() -> Option<Self> {
        let ptr = unsafe { create_handle() };
        if ptr.is_null() {
            None
        } else {
            Some(Self(ptr))
        }
    }

    // Methods take &self or &mut self, not raw pointers
    pub fn do_something(&self) {
        unsafe { handle_operation(self.0); }
    }
}

Converting Between References and Pointers

// Reference to pointer
fn ref_to_ptr(r: &Data) -> *const Data {
    r as *const Data
}

fn mut_ref_to_ptr(r: &mut Data) -> *mut Data {
    r as *mut Data
}

// Slice to pointer
fn slice_to_ptr(s: &[u8]) -> (*const u8, usize) {
    (s.as_ptr(), s.len())
}

// Pointer to reference (unsafe)
unsafe fn ptr_to_ref<'a>(p: *const Data) -> &'a Data {
    &*p
}

unsafe fn ptr_to_mut<'a>(p: *mut Data) -> &'a mut Data {
    &mut *p
}

When to Use Raw Pointers

  • FFI declarations (extern "C")
  • Implementing the unsafe boundary layer
  • When null is a valid value
  • When the pointee might not be valid Rust (e.g., uninitialized)

Checklist

  • Can this parameter be a reference instead of a pointer?
  • Am I checking for null in the unsafe layer?
  • Is the safe API free of raw pointers?
  • Do I use Option<&T> for nullable references?

Related Rules

  • safety-06: Don't expose raw pointers in public APIs
  • ffi-02: Read std::ffi documentation

你可能感兴趣的文章

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/hi9Btg0B