Giao diện
Unsafe Rust
"Unsafe không phải là đối lập của safe. Nó là nền tảng của safe."
1. Sức mạnh của Unsafe
Những gì Unsafe mở khóa
rust
// Unsafe cho phép 5 hành động bị cấm trong safe Rust:
unsafe {
// 1. Dereference raw pointer
let ptr: *const i32 = &42;
let value = *ptr;
// 2. Gọi unsafe function
dangerous_function();
// 3. Truy cập mutable static
GLOBAL_COUNTER += 1;
// 4. Implement unsafe trait
// impl Sync for MyType {}
// 5. Truy cập union fields
// let x = my_union.field;
}Ranh giới Unsafe vs Safe
Ranh giới Safety
┌─────────────────────────────────────────────────────┐
│ │
│ Safe Rust (Compiler xác minh) │
│ ═════════════════════════════ │
│ • References luôn hợp lệ │
│ • Không có data races │
│ • Không có dangling pointers │
│ • Không có buffer overflows │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Unsafe Block (BẠN xác minh) │ │
│ │ ════════════════════════ │ │
│ │ • Raw pointer dereference │ │
│ │ • Gọi FFI │ │
│ │ • Custom invariants │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ Safe code có thể GỌI unsafe code │
│ Nhưng phải ĐÓNG GÓI nó an toàn │
│ │
└─────────────────────────────────────────────────────┘2. Raw Pointers
Các loại Pointer
rust
// Raw pointer bất biến
let x = 42;
let ptr: *const i32 = &x;
// Raw pointer có thể thay đổi
let mut y = 42;
let ptr_mut: *mut i32 = &mut y;
// Khác biệt chính so với references:
// • Có thể null
// • Có thể dangling
// • Có thể alias (nhiều mutable pointers đến cùng data)
// • Không có lifetime tracking
// • Không tự động derefTạo Raw Pointers (Safe)
rust
let x = 42;
// Tạo raw pointers là SAFE
let ptr1: *const i32 = &x; // Từ reference
let ptr2: *const i32 = &x as *const i32; // Cast tường minh
let ptr3 = Box::into_raw(Box::new(42)); // Từ Box
// Từ địa chỉ tùy ý (nguy hiểm!)
let ptr4 = 0x12345 as *const i32;Dereference Raw Pointers (Unsafe)
rust
let x = 42;
let ptr: *const i32 = &x;
// UNSAFE: Dereference yêu cầu unsafe block
unsafe {
println!("Giá trị: {}", *ptr);
}
// Tại sao unsafe? Compiler không thể xác minh:
// • Pointer không null
// • Pointer trỏ đến memory hợp lệ
// • Memory đã được khởi tạo
// • Memory không đang bị mutate ở nơi khácNull Pointers
rust
use std::ptr;
// Tạo null pointer
let null_ptr: *const i32 = ptr::null();
let null_mut: *mut i32 = ptr::null_mut();
// Kiểm tra null
if !null_ptr.is_null() {
unsafe { println!("{}", *null_ptr); }
}
// Rust không có "null reference" - dùng Optional<&T> thay thế
let maybe_ref: Option<&i32> = None; // Thay thế an toànPointer Arithmetic
rust
let arr = [1, 2, 3, 4, 5];
let ptr: *const i32 = arr.as_ptr();
unsafe {
// Offset theo N phần tử (không phải bytes!)
let second = *ptr.offset(1); // arr[1]
let third = *ptr.add(2); // arr[2] - cú pháp gọn hơn
// Offset âm
let ptr_end = ptr.add(4);
let fourth = *ptr_end.sub(1); // arr[3]
// Biến thể wrapping (không UB khi overflow)
let wrapped = ptr.wrapping_add(100);
}3. FFI (Foreign Function Interface)
Gọi C từ Rust
rust
// Khai báo hàm C external
extern "C" {
fn abs(input: i32) -> i32;
fn strlen(s: *const c_char) -> usize;
}
fn main() {
unsafe {
let result = abs(-5);
println!("abs(-5) = {}", result); // 5
}
}Các kiểu FFI
rust
use std::ffi::{c_char, c_int, c_void, CStr, CString};
// Ánh xạ kiểu C
// Kiểu C → Kiểu Rust
// int → c_int (i32 trên hầu hết platforms)
// char* → *const c_char / *mut c_char
// void* → *const c_void / *mut c_void
// size_t → usizeLàm việc với C Strings
rust
use std::ffi::{CStr, CString};
// Rust String → C string (để truyền CHO C)
let rust_string = "Xin chào";
let c_string = CString::new(rust_string).unwrap();
let ptr: *const c_char = c_string.as_ptr();
unsafe {
some_c_function(ptr);
}
// C string → Rust String (từ hàm C)
unsafe {
let c_ptr: *const c_char = get_string_from_c();
// Borrowed view (zero-copy)
let c_str = CStr::from_ptr(c_ptr);
// Convert sang Rust String (copy data)
let rust_string = c_str.to_str().unwrap().to_owned();
}Liên kết thư viện C
rust
// build.rs
fn main() {
// Liên kết thư viện hệ thống
println!("cargo:rustc-link-lib=ssl");
println!("cargo:rustc-link-lib=crypto");
// Hoặc thư viện static
println!("cargo:rustc-link-lib=static=mylib");
println!("cargo:rustc-link-search=native=/path/to/lib");
}
// lib.rs
#[link(name = "mylib")]
extern "C" {
fn my_c_function(x: c_int) -> c_int;
}Expose Rust cho C
rust
// Hàm có thể gọi từ C
#[no_mangle] // Không mangle tên symbol
pub extern "C" fn rust_function(x: i32) -> i32 {
x * 2
}
// Struct với layout tương thích C
#[repr(C)]
pub struct Point {
x: f64,
y: f64,
}
// Tạo header (dùng cbindgen crate)
// Tạo ra: int32_t rust_function(int32_t x);Checklist Safety FFI
rust
// ⚠️ Mọi FFI call đều unsafe vì:
// 1. Rust không thể xác minh memory safety của C
// 2. C có thể không tuân theo aliasing rules của Rust
// 3. C có thể lưu pointers quá lifetime
// 4. C có thể có vấn đề thread-safety
// Pattern đóng gói an toàn:
pub fn safe_abs(x: i32) -> i32 {
unsafe { abs(x) } // Chúng ta biết abs() là pure function
}
// Unsafe nếu không thể đảm bảo safety:
pub unsafe fn dangerous_c_call(ptr: *mut Data) {
// Caller phải đảm bảo ptr hợp lệ
c_function_that_stores_ptr(ptr);
}4. std::mem::transmute
Transmute là gì?
rust
// transmute diễn giải lại bits thành kiểu khác
// CỰC KỲ nguy hiểm - bỏ qua TẤT CẢ type checking
let x: u32 = 0x41424344;
let bytes: [u8; 4] = unsafe { std::mem::transmute(x) };
// bytes = [0x44, 0x43, 0x42, 0x41] trên little-endianBiểu đồ Transmute
transmute<A, B>(a)
┌─────────────────────────────────────────────────┐
│ │
│ Kiểu A: u32 Kiểu B: [u8; 4] │
│ ──────── ──────────── │
│ │
│ 0x41424344 ───▶ [0x44,0x43,0x42,0x41]│
│ │
│ Layout bộ nhớ: │
│ ┌────┬────┬────┬────┐ ┌────┬────┬────┬────┐│
│ │ 44 │ 43 │ 42 │ 41 │ == │ 44 │ 43 │ 42 │ 41 ││
│ └────┴────┴────┴────┘ └────┴────┴────┴────┘│
│ (little-endian) [0] [1] [2] [3] │
│ │
│ Cùng bits, diễn giải khác │
│ │
└─────────────────────────────────────────────────┘Các thay thế an toàn
rust
// ❌ Dùng transmute
let x: f32 = unsafe { std::mem::transmute(0x3f800000u32) };
// ✅ Dùng from_bits (an toàn)
let x: f32 = f32::from_bits(0x3f800000);
// ❌ Transmute cho pointer cast
let ptr: *const u8 = unsafe { std::mem::transmute(some_ptr) };
// ✅ Dùng as cast
let ptr: *const u8 = some_ptr as *const u8;
// ❌ Transmute references
let r: &u32 = unsafe { std::mem::transmute(&x) };
// ✅ Pointer cast rồi dereference (nếu layout tương thích)
let r: &u32 = unsafe { &*((&x) as *const _ as *const u32) };Use Cases hợp lệ của Transmute
rust
// 1. Enum sang repr integer (nếu repr(u8/i32/etc))
#[repr(u8)]
enum Color { R = 0, G = 1, B = 2 }
let c: u8 = unsafe { std::mem::transmute(Color::G) }; // 1
// 2. Function pointer casts (cùng signature)
type FnA = fn(i32) -> i32;
type FnB = fn(i32) -> i32;
let f: FnA = |x| x + 1;
let g: FnB = unsafe { std::mem::transmute(f) };
// 3. Extended lifetime (RẤT nguy hiểm)
// Hầu như không bao giờ đúng!Safety Contract của Transmute
rust
// transmute yêu cầu:
// ✓ Cùng kích thước: size_of::<A>() == size_of::<B>()
// ✓ Bit patterns hợp lệ: B phải chấp nhận mọi bit patterns của A
// ✓ Alignment: alignment của B phải ≤ alignment của A
// Transmutes không hợp lệ (UB):
unsafe {
// Kích thước khác - không biên dịch được
// let x: u64 = std::mem::transmute(0u32);
// Bit pattern không hợp lệ
let b: bool = std::mem::transmute(2u8); // UB! bool phải là 0 hoặc 1
// Reference từ integer
let r: &i32 = std::mem::transmute(0usize); // UB! null reference
}5. Interior Mutability trong Unsafe Context
UnsafeCell
rust
use std::cell::UnsafeCell;
// UnsafeCell là cách DUY NHẤT để mutate qua &T
pub struct MyCell<T> {
value: UnsafeCell<T>,
}
impl<T> MyCell<T> {
pub fn new(value: T) -> Self {
Self { value: UnsafeCell::new(value) }
}
pub fn get(&self) -> *mut T {
self.value.get() // Trả về raw pointer
}
pub fn set(&self, value: T) {
unsafe {
*self.value.get() = value;
}
}
}🎯 Best Practices
Giảm thiểu Unsafe
rust
// ✅ Đóng gói unsafe trong safe API
pub struct SafeBuffer {
data: Vec<u8>,
}
impl SafeBuffer {
pub fn get(&self, index: usize) -> Option<u8> {
// Kiểm tra bounds an toàn
self.data.get(index).copied()
}
pub unsafe fn get_unchecked(&self, index: usize) -> u8 {
// Tài liệu hóa yêu cầu safety
*self.data.get_unchecked(index)
}
}Tài liệu hóa yêu cầu Safety
rust
/// Dereferences raw pointer thành T.
///
/// # Safety
///
/// - `ptr` phải không null
/// - `ptr` phải trỏ đến T hợp lệ, đã khởi tạo
/// - T không được mutate đồng thời
/// - T phải sống trong 'a
pub unsafe fn deref_ptr<'a, T>(ptr: *const T) -> &'a T {
&*ptr
}Các Undefined Behaviors thường gặp
| Hành vi | Ví dụ | Hậu quả |
|---|---|---|
| Null deref | *ptr::null() | Segfault / UB |
| Use after free | Deref freed pointer | Hỏng data |
| Data race | Truy cập mut đồng thời | Hỏng data |
| Giá trị không hợp lệ | transmute(2u8) cho bool | UB |
| Truy cập lệch alignment | Unaligned pointer deref | Crash |