Calling Unsafe Functions
Calling an unsafe function or method—whether defined in Rust with unsafe fn or imported from a foreign language inside an unsafe extern "C" block—requires an unsafe { ... } block at the call site. The unsafe block signifies that the caller has read the callee's # Safety documentation and verified every precondition before invoking the function.
Verifying Preconditions Before the unsafe Block
All runtime checks, bounds assertions, and validation logic belong in safe Rust outside the unsafe block, leaving only the verified call inside unsafe { ... }:
/// Extracts a sub-slice without bounds checks.
///
/// # Safety
/// `start <= end` and `end <= slice.len()` must hold.
unsafe fn subslice_unchecked(slice: &[u8], start: usize, end: usize) -> &[u8] {
// SAFETY: Caller guarantees `start <= end <= slice.len()`.
unsafe {
let ptr = slice.as_ptr().add(start);
std::slice::from_raw_parts(ptr, end - start)
}
}
fn parse_frame(frame: &[u8]) -> Option<(&[u8], &[u8])> {
if frame.len() < 4 {
return None;
}
// SAFETY: We checked `frame.len() >= 4` above, so `0 <= 4 <= frame.len()`
// and `4 <= frame.len() <= frame.len()` both hold.
unsafe {
let header = subslice_unchecked(frame, 0, 4);
let payload = subslice_unchecked(frame, 4, frame.len());
Some((header, payload))
}
}
fn main() {
let raw = [0x01, 0x02, 0x03, 0x04, 0xAA, 0xBB];
println!("parsed: {:?}", parse_frame(&raw));
}
parsed: Some(([1, 2, 3, 4], [170, 187]))
Calling Foreign C Functions (unsafe extern "C")
Foreign functions compiled by C, C++, or assembly compilers are not checked by rustc. In the Rust 2024 edition, extern blocks must be prefixed with unsafe extern "C", and calling any foreign function inside requires an unsafe block unless the foreign declaration is explicitly marked safe fn:
use std::ffi::{CStr, c_char};
unsafe extern "C" {
fn strlen(s: *const c_char) -> usize;
}
fn c_string_length(s: &CStr) -> usize {
// SAFETY: `CStr::as_ptr()` is guaranteed to return a valid, non-null,
// NUL-terminated C string pointer that lives for the duration of `strlen`.
unsafe { strlen(s.as_ptr()) }
}
fn main() {
let greeting: &CStr = c"Hello from C ABI!";
println!("strlen({:?}) = {}", greeting, c_string_length(greeting));
}
strlen("Hello from C ABI!") = 17
Common Standard Library unsafe fn Contracts
Standard Library unsafe fn |
Mandatory Caller Preconditions |
|---|---|
slice::from_raw_parts(data, len) |
data must be non-null and aligned even if len == 0; len * size_of::<T>() bytes must be initialized, in one allocation, $\le \text{isize::MAX}$, and unmutated for 'a |
String::from_utf8_unchecked(bytes) |
bytes must be valid UTF-8 sequences |
str::from_utf8_unchecked(v) |
v must be valid UTF-8 bytes |
Vec::from_raw_parts(ptr, len, cap) |
ptr must have been allocated by the global allocator with the exact alignment of T, len <= cap, and the first len elements initialized |
Vec::set_len(new_len) |
new_len <= capacity(), and elements old_len..new_len must already be initialized |
std::mem::transmute::<Src, Dst>(val) |
size_of::<Src>() == size_of::<Dst>(), and the bit pattern of val must be a valid inhabitant of Dst |
MaybeUninit::assume_init(self) |
The wrapped value must be fully initialized according to T's validity invariant |