Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

unsafe Rust lets you perform a small set of operations the compiler cannot fully verify; it does not switch off Rust’s borrow checker or make undefined behavior acceptable. Use it when low-level work requires those operations, and treat each one as a contract you must uphold.

What does unsafe mean in Rust?

Rust’s unsafe keyword marks code where the programmer must uphold safety guarantees that the compiler cannot prove. It grants five additional capabilities, but ordinary Rust safety checks remain in force. As the Rust Book explains, references used inside unsafe code are still checked.

The keyword has two related roles: it marks operations or implementations that rely on contracts the compiler cannot check, and it records that a programmer has checked those contracts at the point of use. An unsafe block is therefore not a waiver; it is a place where a human takes responsibility for meeting the relevant requirements. See the Rustonomicon’s explanation of how safe and unsafe code interact.

What can you do inside unsafe Rust?

The language adds five capabilities. Each one transfers a specific proof obligation to the programmer; none makes the surrounding code exempt from ordinary checks.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Capability What it permits What you must establish
Dereference raw pointers Read from or write through a *const T or *mut T. The pointer must meet the operation’s validity requirements: for example, it must refer to suitable live storage, be aligned where required, and be used consistently with its lifetime and provenance.
Call unsafe functions or methods Call APIs marked unsafe, including some intrinsics, allocation operations, or foreign-function-interface (FFI) functions. Meet the particular function’s documented preconditions. These vary by API; consult its contract rather than assuming that the unsafe block alone makes the call valid.
Access or modify mutable statics Read or write global mutable state. Maintain the required synchronization and aliasing invariants. Unsynchronized access can make an otherwise plausible implementation unsound.
Implement unsafe traits Provide an implementation for a trait whose contract cannot be checked by the compiler. Make the implementation uphold the trait’s safety promises. For example, Send and Sync concern guarantees about using types across threads.
Access union fields Read or write a field in a union, where storage can be interpreted as different field types. Ensure the chosen field access is valid for the value and storage involved; the surrounding code must establish the guarantees ordinary field access would normally provide.

This is the language’s complete list of extra capabilities, as described in the Rustonomicon. Creating a raw pointer is not the same as dereferencing it: the latter is one of the operations that needs an unsafe context.

Does unsafe disable the borrow checker?

No. Rust continues to check references and other safe operations inside an unsafe block. You cannot use the keyword to make ordinary borrow-checking errors disappear. Instead, it permits specific operations whose safety depends on facts the compiler cannot establish.

That distinction matters because unsafe code often handles raw pointers or shared state, but it still exists within a program governed by Rust’s rules. A block containing one unsafe operation does not make every other operation in that block unchecked.

Can unsafe Rust still cause undefined behavior?

Yes. An unsafe block can be unsound if its contract is violated. Examples include dereferencing a dangling or improperly aligned pointer, breaking pointer-aliasing rules, relying on invalid metadata, using the wrong ABI at an FFI boundary, or calling an API without meeting its preconditions. The Rustonomicon’s account of unsafe operations explains why the compiler cannot make these uses safe merely because they are marked.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Undefined behavior is not a predictable runtime error. Once a program has undefined behavior, the compiler has broad freedom in how it treats the affected code; the result may not be limited to a crash at the point of the mistake. The keyword documents where responsibility lies, not permission to violate Rust’s safety requirements.

When is unsafe Rust appropriate?

Start by asking whether a safe abstraction already provides the capability you need. Reach for unsafe only when the compiler cannot express or verify a necessary invariant and the low-level access is justified by the task. Common settings include operating-system or hardware interaction, FFI, allocators, concurrency primitives, and specialized data structures or optimizations.

  • Use an existing safe API when it covers the task. It avoids making every caller reason about low-level invariants.
  • Consider unsafe for a genuine boundary such as foreign code, raw allocation, hardware access, or an invariant-based primitive that safe Rust cannot implement directly.
  • Weigh the cost against the benefit. Performance or low-level access should justify the added complexity, audit burden, and risk of undefined behavior.
  • Keep the unsafe surface small and make it possible for reviewers to identify exactly which operations depend on which guarantees.

Rust’s systems-programming goals include direct low-level and operating-system interaction, so unsafe has a legitimate role in the ecosystem. Often, its purpose is to implement a library that exposes a safe interface: callers get a simpler, checked API while the library maintains the invariants required by its internal unsafe operations. The Rustonomicon is the official advanced guide to the details involved in writing such code.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How to review an unsafe operation

Treat each unsafe operation as a proof obligation, not as a box to check. The Rustonomicon’s guidance on working with unsafe emphasizes that safety can depend on state and invariants spread across an abstraction, not just on the few lines inside a block.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Read the contract. Identify the exact preconditions of the function, method, trait, or operation. For an unsafe trait implementation, establish what the trait requires of implementors.
  2. State the invariants nearby. Add a safety comment or documentation explaining why the operation is valid and what surrounding code guarantees.
  3. Establish the relevant facts. Depending on the operation, check bounds, pointer alignment and validity, initialization, aliasing, lifetime, synchronization, ABI compatibility, and unwind behavior.
  4. Minimize the unsafe region. Keep the block as small as practical so review can focus on the exact operations and assumptions involved.
  5. Check the public boundary. Expose a safe API only if ordinary safe callers cannot violate the invariants through the inputs and operations the API permits.

A locally convincing pointer check is not enough if the abstraction allows another safe method to invalidate that pointer before use. Review the whole invariant: how it is created, maintained, and prevented from being broken by callers or other code paths.

What to read next

The Rust Book’s Unsafe Rust chapter gives the core language overview. For deeper treatment of contracts, pointer rules, and building safe abstractions on unsafe primitives, continue with the Rustonomicon and its chapters on how safe and unsafe code interact and working with unsafe.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.