Skip to main content

Unsafe Rust Features

Oxide supports Rust's unsafe features for low-level programming, FFI, and performance-critical code.

Unsafe Blocks

Unsafe blocks allow operations that the compiler cannot verify:

fn main() {
let value = 42

let result = unsafe {
let ptr: *const Int = &value as *const Int
*ptr // Dereference raw pointer
}

println!("Result: $result")
}

Transpiles to:

fn main() {
let value = 42;

let result = unsafe {
let ptr: *const isize = &value as *const isize;
*ptr
};

println!("Result: {}", result);
}

What Requires Unsafe

  • Dereferencing raw pointers
  • Calling unsafe functions
  • Accessing mutable statics
  • Accessing union fields
  • Implementing unsafe traits

Unsafe Functions

Functions that require unsafe to call:

unsafe fn readPointer(ptr: *const Int): Int {
*ptr
}

fn main() {
let value = 42
let result = unsafe {
readPointer(&value as *const Int)
}
}

Raw Pointers

let value = 42

// Create raw pointers
let ptr: *const Int = &value as *const Int // Immutable
let mutPtr: *mut Int = &var value as *mut Int // Mutable

// Use in unsafe block
unsafe {
let read = *ptr
*mutPtr = 100
}

Pointer Operations

unsafe {
// Offset
let next = ptr.offset(1)

// Null check
if !ptr.is_null() {
let value = *ptr
}

// Read/write
let value = ptr.read()
mutPtr.write(42)
}

Extern Functions (FFI)

Single Extern Function

// Declaration (no body)
extern "C" fn strlen(s: *const Char): UInt;

// With implementation (for export)
extern "C" fn myExport(x: Int): Int {
x + 1
}

Extern Blocks

Group foreign function declarations:

extern "C" {
fn strlen(s: *const Char): UInt
fn getenv(name: *const Char): *const Char
fn printf(format: *const Char, ...): Int
}

Supported ABIs:

  • "C" - Standard C calling convention (default)
  • "stdcall" - Windows stdcall
  • "system" - Platform system ABI
  • Others supported by Rust

Calling FFI Functions

fn main() {
let name = "PATH\0" // Null-terminated

unsafe {
let value = getenv(name.as_ptr() as *const Char)
if !value.is_null() {
// Convert to Rust string
let s = std::ffi::CStr::from_ptr(value)
println!("PATH = ${s.to_string_lossy()}")
}
}
}

Unions

Unions provide low-level memory representation. Must use unsafe keyword.

Unions use brace syntax Union { field: value } for initialization (unlike structs which use parenthesis syntax Struct(field = value)). The LSP will show an error if you use the wrong syntax:

unsafe union IntOrFloat {
intVal: Int,
floatVal: Float64
}

fn main() {
// All union operations require unsafe
unsafe {
let data = IntOrFloat { intVal: 42 } // Note: brace syntax

// Reading any field
let i = data.intVal
let f = data.floatVal // Reinterprets bits as float
}
}
warning

Accessing the wrong union field is undefined behavior. The unsafe keyword requirement makes this explicit.

Generic Unions

unsafe union Value<T: Copy> {
val: T,
ptr: *const T
}

FFI Unions

Commonly used for C interop:

@[repr(C)]
unsafe union CValue {
i: Int32,
f: Float32,
ptr: *mut ()
}

Mutable Statics

Global mutable state requires unsafe:

static var COUNTER: Int = 0

fn increment(): Int {
unsafe {
COUNTER += 1
COUNTER
}
}

Unsafe Traits

Traits that require unsafe to implement:

unsafe trait Marker { }

unsafe extension MyType: Marker { }

Transmute

Reinterpret bytes as another type:

fn floatBits(f: Float32): UInt32 {
unsafe {
std::mem::transmute(f)
}
}
danger

transmute is extremely dangerous. Use only when absolutely necessary and you understand the memory layout of both types.

Safety Guidelines

Minimize Unsafe Code

Wrap unsafe operations in safe abstractions:

// Good: Safe wrapper around unsafe
struct SafeBuffer {
ptr: *mut UInt8,
len: UInt
}

extension SafeBuffer {
fn get(index: UInt): UInt8? {
if index < self.len {
unsafe { Some(*self.ptr.offset(index as Int)) }
} else {
null
}
}
}

Document Invariants

/// # Safety
///
/// - `ptr` must point to valid memory
/// - Memory must be initialized
/// - Pointer must be properly aligned
unsafe fn processPointer(ptr: *const Data) {
// ...
}

Review Carefully

  • Unsafe code can cause undefined behavior
  • Memory corruption can be silent
  • Data races can cause random failures
  • Consider using safe alternatives first

Safe Alternatives

Before using unsafe, consider:

Instead of...Try...
Raw pointersReferences (&T, &var T)
transmuteSpecific conversion methods
UnionsEnums with variants
Manual memoryVec, Box, smart pointers

See Also