🦀 Unsafe Rust in TuskLang
Unsafe Rust in TuskLang
⚠️ Unsafe Foundation
Unsafe Rust provides the ability to bypass Rust's safety guarantees when necessary, enabling low-level programming, FFI integration, and performance optimizations. This guide covers raw pointers, unsafe blocks, and safe abstractions over unsafe code.
🏗️ Unsafe Architecture
Unsafe Principles
[unsafe_principles]
safety_contracts: true
unsafe_blocks: true
raw_pointers: true
ffi_integration: true[unsafe_patterns]
safe_abstractions: true
unsafe_apis: true
memory_safety: true
undefined_behavior: true
Unsafe Components
[unsafe_components]
raw_pointers: "unsafe"
unsafe_functions: "unsafe_blocks"
ffi: "foreign_functions"
static_mut: "global_state"
🔧 Raw Pointers
Raw Pointer Types
[raw_pointer_types]
const_pointers: true
mut_pointers: true
pointer_arithmetic: true[raw_pointer_implementation]
// Raw pointer basics
pub fn raw_pointer_example() {
let mut x = 5;
let raw_ptr = &mut x as *mut i32;
unsafe {
println!("Value through raw pointer: {}", *raw_ptr);
*raw_ptr = 10;
println!("Modified value: {}", *raw_ptr);
}
}
// Raw pointer arithmetic
pub fn pointer_arithmetic_example() {
let mut arr = [1, 2, 3, 4, 5];
let ptr = arr.as_mut_ptr();
unsafe {
for i in 0..5 {
let element_ptr = ptr.add(i);
println!("Element {}: {}", i, *element_ptr);
}
}
}
// Null pointers
pub fn null_pointer_example() {
let null_ptr: *const i32 = std::ptr::null();
unsafe {
if null_ptr.is_null() {
println!("Pointer is null");
} else {
println!("Pointer is not null: {}", *null_ptr);
}
}
}
// Raw pointer to struct
#[derive(Debug)]
pub struct Point {
x: f64,
y: f64,
}
pub fn struct_pointer_example() {
let mut point = Point { x: 1.0, y: 2.0 };
let ptr = &mut point as *mut Point;
unsafe {
println!("Point: {:?}", *ptr);
(*ptr).x = 3.0;
(*ptr).y = 4.0;
println!("Modified point: {:?}", *ptr);
}
}
Pointer Safety
[pointer_safety]
dereference_safety: true
aliasing_rules: true
lifetime_management: true[pointer_safety_implementation]
// Safe dereferencing
pub fn safe_dereference_example() {
let mut data = vec![1, 2, 3, 4, 5];
let ptr = data.as_mut_ptr();
unsafe {
// Safe because we know the pointer is valid
for i in 0..data.len() {
let element_ptr = ptr.add(i);
println!("Element {}: {}", i, *element_ptr);
}
}
}
// Aliasing prevention
pub fn aliasing_example() {
let mut data = vec![1, 2, 3, 4, 5];
let ptr1 = data.as_mut_ptr();
let ptr2 = data.as_mut_ptr();
unsafe {
// This is safe because we're not aliasing mutable references
*ptr1 = 10;
*ptr2 = 20;
}
}
// Lifetime management with raw pointers
pub struct RawBuffer {
ptr: *mut u8,
len: usize,
}
impl RawBuffer {
pub fn new(size: usize) -> Self {
let layout = std::alloc::Layout::from_size_align(size, 8).unwrap();
let ptr = unsafe { std::alloc::alloc(layout) };
Self {
ptr,
len: size,
}
}
pub fn as_slice(&self) -> &[u8] {
unsafe {
std::slice::from_raw_parts(self.ptr, self.len)
}
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe {
std::slice::from_raw_parts_mut(self.ptr, self.len)
}
}
}
impl Drop for RawBuffer {
fn drop(&mut self) {
let layout = std::alloc::Layout::from_size_align(self.len, 8).unwrap();
unsafe {
std::alloc::dealloc(self.ptr, layout);
}
}
}
🔄 Unsafe Functions
Unsafe Function Design
[unsafe_function_design]
safety_contracts: true
documentation: true
invariant_checks: true[unsafe_function_implementation]
// Unsafe function with safety contract
/// # Safety
///
/// The caller must ensure that:
/// - ptr
is not null
/// - ptr
points to valid memory
/// - ptr
is aligned for T
/// - ptr
is valid for reads
pub unsafe fn read_value<T>(ptr: *const T) -> T {
// Safety: caller guarantees ptr is valid
ptr.read()
}
// Unsafe function with runtime checks
pub fn safe_read_value<T>(ptr: *const T) -> Option<T> {
if ptr.is_null() {
return None;
}
unsafe {
Some(ptr.read())
}
}
// Unsafe function with invariant checking
pub struct SafeArray<T> {
ptr: *mut T,
len: usize,
}
impl<T> SafeArray<T> {
pub fn new(len: usize) -> Self {
let layout = std::alloc::Layout::array::<T>(len).unwrap();
let ptr = unsafe { std::alloc::alloc(layout) as *mut T };
Self { ptr, len }
}
/// # Safety
///
/// The caller must ensure that:
/// - index < self.len
pub unsafe fn get_unchecked(&self, index: usize) -> &T {
&*self.ptr.add(index)
}
/// # Safety
///
/// The caller must ensure that:
/// - index < self.len
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
&mut *self.ptr.add(index)
}
// Safe wrapper
pub fn get(&self, index: usize) -> Option<&T> {
if index < self.len {
unsafe { Some(self.get_unchecked(index)) }
} else {
None
}
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
if index < self.len {
unsafe { Some(self.get_unchecked_mut(index)) }
} else {
None
}
}
}
impl<T> Drop for SafeArray<T> {
fn drop(&mut self) {
let layout = std::alloc::Layout::array::<T>(self.len).unwrap();
unsafe {
std::alloc::dealloc(self.ptr as *mut u8, layout);
}
}
}
Unsafe Traits
[unsafe_traits]
send_sync: true
unsafe_trait_impl: true
safety_guarantees: true[unsafe_trait_implementation]
// Unsafe trait
pub unsafe trait UnsafeTrait {
fn unsafe_method(&self);
}
// Safe wrapper trait
pub trait SafeTrait {
fn safe_method(&self);
}
// Implementation of unsafe trait
pub struct UnsafeStruct {
data: *mut i32,
}
unsafe impl UnsafeTrait for UnsafeStruct {
fn unsafe_method(&self) {
unsafe {
if !self.data.is_null() {
println!("Unsafe value: {}", *self.data);
}
}
}
}
// Safe implementation
impl SafeTrait for UnsafeStruct {
fn safe_method(&self) {
// Safe wrapper around unsafe code
if !self.data.is_null() {
unsafe {
println!("Safe access: {}", *self.data);
}
}
}
}
// Send and Sync implementations
unsafe impl Send for UnsafeStruct {}
unsafe impl Sync for UnsafeStruct {}
🌐 FFI Integration
C FFI
[ffi_integration]
c_functions: true
c_structs: true
callback_functions: true[ffi_implementation]
use std::ffi::{c_char, CStr, CString};
// C function declarations
#[link(name = "c")]
extern "C" {
fn strlen(s: *const c_char) -> usize;
fn printf(format: *const c_char, ...) -> i32;
}
// Safe wrapper for C functions
pub fn safe_strlen(s: &str) -> usize {
let c_string = CString::new(s).unwrap();
unsafe {
strlen(c_string.as_ptr())
}
}
pub fn safe_printf(format: &str) {
let c_format = CString::new(format).unwrap();
unsafe {
printf(c_format.as_ptr());
}
}
// C struct integration
#[repr(C)]
pub struct CPoint {
x: f64,
y: f64,
}
#[link(name = "math")]
extern "C" {
fn distance(p1: const CPoint, p2: const CPoint) -> f64;
}
pub fn safe_distance(p1: &CPoint, p2: &CPoint) -> f64 {
unsafe {
distance(p1, p2)
}
}
// Callback functions
pub type CallbackFn = extern "C" fn(i32) -> i32;
#[link(name = "callback")]
extern "C" {
fn register_callback(callback: CallbackFn);
fn call_callback(value: i32) -> i32;
}
pub extern "C" fn rust_callback(value: i32) -> i32 {
value * 2
}
pub fn register_rust_callback() {
unsafe {
register_callback(rust_callback);
}
}
pub fn call_rust_callback(value: i32) -> i32 {
unsafe {
call_callback(value)
}
}
System Calls
[system_calls]
syscalls: true
low_level_io: true
memory_mapping: true[system_calls_implementation]
use std::os::unix::io::{AsRawFd, RawFd};
// Low-level file operations
pub struct LowLevelFile {
fd: RawFd,
}
impl LowLevelFile {
pub fn open(path: &str, flags: i32, mode: u32) -> Result<Self, i32> {
let c_path = CString::new(path).unwrap();
let fd = unsafe {
libc::open(c_path.as_ptr(), flags, mode)
};
if fd == -1 {
Err(std::io::Error::last_os_error().raw_os_error().unwrap())
} else {
Ok(Self { fd })
}
}
pub fn read(&self, buffer: &mut [u8]) -> Result<usize, i32> {
let result = unsafe {
libc::read(
self.fd,
buffer.as_mut_ptr() as *mut libc::c_void,
buffer.len(),
)
};
if result == -1 {
Err(std::io::Error::last_os_error().raw_os_error().unwrap())
} else {
Ok(result as usize)
}
}
pub fn write(&self, buffer: &[u8]) -> Result<usize, i32> {
let result = unsafe {
libc::write(
self.fd,
buffer.as_ptr() as *const libc::c_void,
buffer.len(),
)
};
if result == -1 {
Err(std::io::Error::last_os_error().raw_os_error().unwrap())
} else {
Ok(result as usize)
}
}
}
impl Drop for LowLevelFile {
fn drop(&mut self) {
unsafe {
libc::close(self.fd);
}
}
}
// Memory mapping
pub struct MemoryMap {
ptr: *mut u8,
len: usize,
}
impl MemoryMap {
pub fn new(fd: RawFd, len: usize, offset: i64) -> Result<Self, i32> {
let ptr = unsafe {
libc::mmap(
std::ptr::null_mut(),
len,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd,
offset,
) as *mut u8
};
if ptr == std::ptr::null_mut() {
Err(std::io::Error::last_os_error().raw_os_error().unwrap())
} else {
Ok(Self { ptr, len })
}
}
pub fn as_slice(&self) -> &[u8] {
unsafe {
std::slice::from_raw_parts(self.ptr, self.len)
}
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe {
std::slice::from_raw_parts_mut(self.ptr, self.len)
}
}
}
impl Drop for MemoryMap {
fn drop(&mut self) {
unsafe {
libc::munmap(self.ptr as *mut libc::c_void, self.len);
}
}
}
🔒 Static Mutable State
Global State Management
[global_state]
static_mut: true
thread_local: true
atomic_operations: true[global_state_implementation]
use std::sync::atomic::{AtomicUsize, Ordering};
use std::cell::UnsafeCell;
// Static mutable variable
static mut GLOBAL_COUNTER: usize = 0;
pub fn increment_global_counter() {
unsafe {
GLOBAL_COUNTER += 1;
}
}
pub fn get_global_counter() -> usize {
unsafe {
GLOBAL_COUNTER
}
}
// Thread-local storage
thread_local! {
static THREAD_COUNTER: UnsafeCell<usize> = UnsafeCell::new(0);
}
pub fn increment_thread_counter() {
THREAD_COUNTER.with(|counter| {
unsafe {
*counter.get() += 1;
}
});
}
pub fn get_thread_counter() -> usize {
THREAD_COUNTER.with(|counter| {
unsafe {
*counter.get()
}
})
}
// Atomic global state
static ATOMIC_COUNTER: AtomicUsize = AtomicUsize::new(0);
pub fn increment_atomic_counter() {
ATOMIC_COUNTER.fetch_add(1, Ordering::SeqCst);
}
pub fn get_atomic_counter() -> usize {
ATOMIC_COUNTER.load(Ordering::SeqCst)
}
// Singleton pattern with unsafe
pub struct Singleton {
data: UnsafeCell<String>,
}
impl Singleton {
pub fn new() -> Self {
Self {
data: UnsafeCell::new(String::new()),
}
}
pub fn set_data(&self, data: String) {
unsafe {
*self.data.get() = data;
}
}
pub fn get_data(&self) -> String {
unsafe {
(*self.data.get()).clone()
}
}
}
unsafe impl Send for Singleton {}
unsafe impl Sync for Singleton {}
static mut SINGLETON: Option<Singleton> = None;
pub fn get_singleton() -> &'static Singleton {
unsafe {
if SINGLETON.is_none() {
SINGLETON = Some(Singleton::new());
}
SINGLETON.as_ref().unwrap()
}
}
🔄 Safe Abstractions
Wrapping Unsafe Code
[safe_abstractions]
unsafe_wrappers: true
type_safety: true
error_handling: true[safe_abstractions_implementation]
// Safe wrapper around raw pointer
pub struct SafePtr<T> {
ptr: *mut T,
len: usize,
}
impl<T> SafePtr<T> {
pub fn new(len: usize) -> Self {
let layout = std::alloc::Layout::array::<T>(len).unwrap();
let ptr = unsafe { std::alloc::alloc(layout) as *mut T };
Self { ptr, len }
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn get(&self, index: usize) -> Option<&T> {
if index < self.len {
unsafe {
Some(&*self.ptr.add(index))
}
} else {
None
}
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
if index < self.len {
unsafe {
Some(&mut *self.ptr.add(index))
}
} else {
None
}
}
pub fn iter(&self) -> SafePtrIter<T> {
SafePtrIter {
ptr: self.ptr,
len: self.len,
index: 0,
}
}
}
impl<T> Drop for SafePtr<T> {
fn drop(&mut self) {
let layout = std::alloc::Layout::array::<T>(self.len).unwrap();
unsafe {
std::alloc::dealloc(self.ptr as *mut u8, layout);
}
}
}
pub struct SafePtrIter<T> {
ptr: *mut T,
len: usize,
index: usize,
}
impl<T> Iterator for SafePtrIter<T> {
type Item = &'static T;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.len {
let item = unsafe { &*self.ptr.add(self.index) };
self.index += 1;
Some(item)
} else {
None
}
}
}
// Safe wrapper for FFI
pub struct SafeCString {
inner: CString,
}
impl SafeCString {
pub fn new(s: &str) -> Result<Self, std::ffi::NulError> {
Ok(Self {
inner: CString::new(s)?,
})
}
pub fn as_ptr(&self) -> *const c_char {
self.inner.as_ptr()
}
pub fn to_string_lossy(&self) -> std::borrow::Cow<str> {
self.inner.to_string_lossy()
}
}
// Safe wrapper for system calls
pub struct SafeFile {
inner: LowLevelFile,
}
impl SafeFile {
pub fn open(path: &str) -> Result<Self, std::io::Error> {
let file = LowLevelFile::open(path, libc::O_RDWR | libc::O_CREAT, 0o644)
.map_err(|e| std::io::Error::from_raw_os_error(e))?;
Ok(Self { inner: file })
}
pub fn read(&self, buffer: &mut [u8]) -> Result<usize, std::io::Error> {
self.inner.read(buffer)
.map_err(|e| std::io::Error::from_raw_os_error(e))
}
pub fn write(&self, buffer: &[u8]) -> Result<usize, std::io::Error> {
self.inner.write(buffer)
.map_err(|e| std::io::Error::from_raw_os_error(e))
}
}
🎯 Best Practices
1. Safety Contracts
- Document all safety requirements - Use runtime checks when possible - Provide safe wrappers - Test unsafe code thoroughly2. Memory Safety
- Validate all pointers - Check bounds before access - Use proper alignment - Avoid undefined behavior3. FFI Safety
- Validate C function contracts - Handle null pointers - Manage memory properly - Use proper calling conventions4. Error Handling
- Return Result types - Provide meaningful errors - Handle edge cases - Use proper error propagation5. Testing
- Test unsafe code extensively - Use Miri for validation - Test edge cases - Verify safety contractsUnsafe Rust in TuskLang provides the power to write low-level code while maintaining the ability to create safe abstractions that leverage Rust's safety guarantees.