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
}
}
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)
}
}
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 pointers | References (&T, &var T) |
transmute | Specific conversion methods |
| Unions | Enums with variants |
| Manual memory | Vec, Box, smart pointers |
See Also
- Ownership - Safe borrowing patterns
- FFI Documentation - Rust FFI guide
- Attributes -
#[repr(C)]and other FFI attributes