Rust Unsafe and FFI
Use this when the code must uphold invariants the compiler cannot check.
Working stance
- Safe wrapper first;
unsafeis the narrow implementation detail. - Write the invariant list before writing the block.
- Every
unsafeblock should justify itself in plain language. - FFI boundaries must define ownership, lifetime, layout, and panic policy.
- If a safe design is available and fast enough, prefer it.
Decision surface
- Non-null raw ownership handle: use
NonNull<T>. - Deferred initialization: use
MaybeUninit<T>. - C ABI interop: use an explicit
repr(C)contract where required. - Public low-level capability: build a safe wrapper and expose a narrow
unsafe fnonly when the caller truly must uphold the contract. - Sharing across threads: prove
SendandSync; never assume them.
Red flags
unsafeappears only to silence the borrow checker,transmuteor raw-pointer tricks appear before a safe conversion or wrapper was ruled out,- pointer validity and aliasing rules are not written down,
CString::as_ptr()outlives the owningCString,- FFI code can panic across the boundary,
- manual
SendorSyncimpls appear without a crisp argument, - shared mutation is built without
UnsafeCellor a wrapper that uses it.
Read safety-comment-template.md,
ffi-boundaries.md,
maybeuninit-and-nonnull.md, and
unsafecell-and-interior-mutability.md
before adding new unsafe blocks or reviewing old ones. The verification
skill (rust-verification) covers when to reach for Miri and loom.
Source: leynos/rust-skill — distributed by TomeVault.