Reading — step 1 of 4
Learn
~1 min readMacros and Unsafe
unsafe lets you do things the borrow checker can't verify. Used in:
- Calling C libraries (FFI)
- Implementing data structures (linked lists, raw pointers)
- Hardware/OS interfaces
- Performance-critical paths where you've manually verified correctness
The 5 unsafe "superpowers":
- Dereference a raw pointer (
*ptr) - Call an
unsafefunction - Access or modify a mutable static variable
- Implement an
unsafetrait - Access fields of
unions
Raw pointers:
let x = 5;
let r1: *const i32 = &x;
let r2: *mut i32 = &x as *const i32 as *mut i32;
unsafe {
println!("{}", *r1);
// *r2 = 10; // would be a data race; *r2 must point to actual mutable memory
}
Raw pointers can be null, dangling, unaligned. Rust doesn't track them. They're for FFI mostly.
FFI to C:
extern "C" {
fn abs(x: i32) -> i32; // declared from libc
}
fn main() {
let result = unsafe { abs(-5) };
println!("{}", result);
}
Calling unsafe Rust functions:
unsafe fn dangerous() {
println!("trust me");
}
fn main() {
unsafe { dangerous(); }
}
unsafe block discipline:
- Wrap as small a region as possible
- Add a comment explaining WHY it's safe
- Provide a safe wrapper API; users shouldn't need
unsafe
// SAFETY: We just allocated `buf`, so `ptr` is valid for `len` bytes.
unsafe { ptr::write_bytes(ptr, 0, len); }
std::mem::transmute — bit-cast one type to another (extremely unsafe):
let bytes: [u8; 4] = unsafe { std::mem::transmute(42i32) };
Avoid unsafe when you can. Modern Rust has many sound APIs (e.g., slice::from_raw_parts, Cell::as_ptr) that wrap unsafe internally. Reach for raw unsafe only when no safe API exists.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…