1. 1. Introduction
  2. 2. Getting Started
  3. 3. Tutorial: Guessing Game
  4. 4. Syntax and Semantics
    1. 4.1. Variable Bindings
    2. 4.2. Functions
    3. 4.3. Primitive Types
    4. 4.4. Comments
    5. 4.5. if
    6. 4.6. Loops
    7. 4.7. Vectors
    8. 4.8. Ownership
    9. 4.9. References and Borrowing
    10. 4.10. Lifetimes
    11. 4.11. Mutability
    12. 4.12. Structs
    13. 4.13. Enums
    14. 4.14. Match
    15. 4.15. Patterns
    16. 4.16. Method Syntax
    17. 4.17. Strings
    18. 4.18. Generics
    19. 4.19. Traits
    20. 4.20. Drop
    21. 4.21. if let
    22. 4.22. Trait Objects
    23. 4.23. Closures
    24. 4.24. Universal Function Call Syntax
    25. 4.25. Crates and Modules
    26. 4.26. `const` and `static`
    27. 4.27. Attributes
    28. 4.28. `type` aliases
    29. 4.29. Casting between types
    30. 4.30. Associated Types
    31. 4.31. Unsized Types
    32. 4.32. Operators and Overloading
    33. 4.33. Deref coercions
    34. 4.34. Macros
    35. 4.35. Raw Pointers
    36. 4.36. `unsafe`
  5. 5. Effective Rust
    1. 5.1. The Stack and the Heap
    2. 5.2. Testing
    3. 5.3. Conditional Compilation
    4. 5.4. Documentation
    5. 5.5. Iterators
    6. 5.6. Concurrency
    7. 5.7. Error Handling
    8. 5.8. Choosing your Guarantees
    9. 5.9. FFI
    10. 5.10. Borrow and AsRef
    11. 5.11. Release Channels
    12. 5.12. Using Rust without the standard library
  6. 6. Nightly Rust
    1. 6.1. Compiler Plugins
    2. 6.2. Inline Assembly
    3. 6.3. No stdlib
    4. 6.4. Intrinsics
    5. 6.5. Lang items
    6. 6.6. Advanced linking
    7. 6.7. Benchmark Tests
    8. 6.8. Box Syntax and Patterns
    9. 6.9. Slice Patterns
    10. 6.10. Associated Constants
    11. 6.11. Custom Allocators
  7. 7. Glossary
  8. 8. Syntax Index
  9. 9. Bibliography


Rust’s main draw is its powerful static guarantees about behavior. But safety checks are conservative by nature: there are some programs that are actually safe, but the compiler is not able to verify this is true. To write these kinds of programs, we need to tell the compiler to relax its restrictions a bit. For this, Rust has a keyword, unsafe. Code using unsafe has fewer restrictions than normal code does.

Let’s go over the syntax, and then we’ll talk semantics. unsafe is used in four contexts. The first one is to mark a function as unsafe:

unsafe fn danger_will_robinson() {
    // scary stuff

All functions called from FFI must be marked as unsafe, for example. The second use of unsafe is an unsafe block:

unsafe {
    // scary stuff

The third is for unsafe traits:

unsafe trait Scary { }Run

And the fourth is for implementing one of those traits:

unsafe impl Scary for i32 {}Run

It’s important to be able to explicitly delineate code that may have bugs that cause big problems. If a Rust program segfaults, you can be sure the cause is related to something marked unsafe.

What does ‘safe’ mean?

Safe, in the context of Rust, means ‘doesn’t do anything unsafe’. It’s also important to know that there are certain behaviors that are probably not desirable in your code, but are expressly not unsafe:

Rust cannot prevent all kinds of software problems. Buggy code can and will be written in Rust. These things aren’t great, but they don’t qualify as unsafe specifically.

In addition, the following are all undefined behaviors in Rust, and must be avoided, even when writing unsafe code:

Unsafe Superpowers

In both unsafe functions and unsafe blocks, Rust will let you do three things that you normally can not do. Just three. Here they are:

  1. Access or update a static mutable variable.
  2. Dereference a raw pointer.
  3. Call unsafe functions. This is the most powerful ability.

That’s it. It’s important that unsafe does not, for example, ‘turn off the borrow checker’. Adding unsafe to some random Rust code doesn’t change its semantics, it won’t start accepting anything. But it will let you write things that do break some of the rules.

You will also encounter the unsafe keyword when writing bindings to foreign (non-Rust) interfaces. You're encouraged to write a safe, native Rust interface around the methods provided by the library.

Let’s go over the basic three abilities listed, in order.

Access or update a static mut

Rust has a feature called ‘static mut’ which allows for mutable global state. Doing so can cause a data race, and as such is inherently not safe. For more details, see the static section of the book.

Dereference a raw pointer

Raw pointers let you do arbitrary pointer arithmetic, and can cause a number of different memory safety and security issues. In some senses, the ability to dereference an arbitrary pointer is one of the most dangerous things you can do. For more on raw pointers, see their section of the book.

Call unsafe functions

This last ability works with both aspects of unsafe: you can only call functions marked unsafe from inside an unsafe block.

This ability is powerful and varied. Rust exposes some compiler intrinsics as unsafe functions, and some unsafe functions bypass safety checks, trading safety for speed.

I’ll repeat again: even though you can do arbitrary things in unsafe blocks and functions doesn’t mean you should. The compiler will act as though you’re upholding its invariants, so be careful!