Unsafe Rust
Unsafe Rust cho phép bạn bỏ qua một số kiểm tra an toàn của compiler để đạt được hiệu suất cao hơn hoặc tương tác với code bên ngoài Rust. Tuy nhiên, bạn cần chịu trách nhiệm đảm bảo tính đúng đắn của code.
Tại sao cần Unsafe?
Rust’s safety guarantees có một số giới hạn:
- Performance: Đôi khi cần tối ưu bỏ qua bounds checking
- FFI: Gọi C libraries không an toàn theo định nghĩa
- Low-level operations: Thao tác trực tiếp với memory
- Hardware interaction: Embedded programming, kernel development
Năm Unsafe Superpowers
Unsafe Rust cho phép bạn làm 5 điều mà safe Rust không cho phép:
1. Dereferencing Raw Pointers
fn main() {
let mut num = 5;
// Tạo raw pointers (an toàn)
let r1 = &num as *const i32; // immutable raw pointer
let r2 = &mut num as *mut i32; // mutable raw pointer
// Dereferencing raw pointer (KHÔNG an toàn)
unsafe {
println!("r1 points to: {}", *r1);
*r2 = 10;
println!("r2 changed value to: {}", *r2);
}
}
Nguy hiểm: Raw pointers có thể null, dangling, hoặc unaligned!
2. Gọi Unsafe Functions
// Khai báo unsafe function
unsafe fn dangerous() {
println!("Doing something dangerous!");
}
fn main() {
// Phải gọi trong unsafe block
unsafe {
dangerous();
}
}
3. Truy cập/Modify Mutable Static Variables
static mut COUNTER: u32 = 0;
fn add_to_counter(inc: u32) {
unsafe {
COUNTER += inc; // Có thể gây data race!
}
}
fn main() {
add_to_counter(3);
unsafe {
println!("COUNTER: {}", COUNTER);
}
}
Cảnh báo: Mutable static có thể gây data races trong multi-threaded code!
4. Implementing Unsafe Traits
// Unsafe trait
unsafe trait Foo {
fn foo(&self);
}
struct MyType;
// Implement unsafe trait
unsafe impl Foo for MyType {
fn foo(&self) {
println!("Foo!");
}
}
fn main() {
let my = MyType;
my.foo(); // Gọi method không cần unsafe
}
5. Accessing Fields of Unions
union MyUnion {
f1: u32,
f2: f32,
}
fn main() {
let u = MyUnion { f1: 1 };
unsafe {
// Không biết field nào valid
println!("u.f1: {}", u.f1);
// println!("u.f2: {}", u.f2); // Có thể in giá trị garbage
}
}
Best Practices
1. Minimize Unsafe Scope
#![allow(unused)]
fn main() {
// ❌ Tránh: Unsafe block lớn
unsafe {
// 100 lines of code
}
// ✅ Tốt hơn: Unsafe block nhỏ nhất có thể
let result = unsafe {
*raw_pointer
};
// Xử lý result ở safe code
}
2. Abstraction Pattern
Wrap unsafe code trong safe API:
pub struct SafeVec<T> {
ptr: *mut T,
len: usize,
capacity: usize,
}
impl<T> SafeVec<T> {
// Safe API
pub fn push(&mut self, value: T) {
if self.len == self.capacity {
self.grow();
}
unsafe {
// Unsafe implementation ẩn bên trong
std::ptr::write(self.ptr.add(self.len), value);
}
self.len += 1;
}
fn grow(&mut self) {
// Implementation with unsafe code...
}
}
3. Document Safety Invariants
#![allow(unused)]
fn main() {
/// # Safety
///
/// `ptr` must be valid for reads of `len` elements of type `T`.
/// `ptr` must be properly aligned.
/// `len` must not exceed the allocation size.
unsafe fn read_slice<T>(ptr: *const T, len: usize) -> Vec<T> {
let mut vec = Vec::with_capacity(len);
std::ptr::copy_nonoverlapping(ptr, vec.as_mut_ptr(), len);
vec.set_len(len);
vec
}
}
Common Patterns
Transmute - Chuyển đổi Type
use std::mem;
fn main() {
// Chuyển f32 thành u32 bit pattern
let f: f32 = 3.14;
let bits: u32 = unsafe {
mem::transmute(f)
};
println!("f32 {:.2} as u32 bits: 0x{:08x}", f, bits);
}
Nguy hiểm: Transmute không kiểm tra gì cả. Rất dễ gây UB (Undefined Behavior)!
Raw Pointer Arithmetic
fn main() {
let arr = [1, 2, 3, 4, 5];
let ptr = arr.as_ptr();
unsafe {
// Pointer arithmetic
for i in 0..5 {
let elem = ptr.add(i);
println!("Element {}: {}", i, *elem);
}
}
}
Undefined Behavior (UB)
Actions gây UB trong unsafe code:
- ❌ Dereferencing null/dangling pointers
- ❌ Data races
- ❌ Breaking pointer aliasing rules
- ❌ Using uninitialized memory
- ❌ Invalid values (e.g.,
boolkhông phải 0 hoặc 1) - ❌ Calling foreign functions không đúng cách
UB = Game Over: Compiler có thể assume UB never happens và optimize sai!
Tools để Debug Unsafe Code
1. Miri
Interpreter phát hiện UB:
cargo install miri
cargo +nightly miri test
2. AddressSanitizer
Phát hiện memory errors:
RUSTFLAGS="-Z sanitizer=address" cargo +nightly build
3. ThreadSanitizer
Phát hiện data races:
RUSTFLAGS="-Z sanitizer=thread" cargo +nightly build
Khi nào dùng Unsafe?
✅ Hợp lý:
- Implement low-level data structures (Vec, HashMap)
- FFI bindings
- Optimize performance critical code
- Interact với hardware
❌ Tránh:
- Shortcuts vì lười implement trait bounds
- “Compiler bắt bẻ quá” - suy nghĩ lại design!
- Chưa hiểu rõ tại sao cần
Nguyên tắc Vàng
“Unsafe không có nghĩa là unreviewed”
Mọi unsafe block cần:
- Comment giải thích TẠI SAO cần unsafe
- Document safety invariants
- Code review kỹ càng
- Test coverage cao
Tham khảo
- The Rustonomicon - The Dark Arts of Unsafe Rust
- Rust Reference - Unsafe
- Miri Documentation