---
title: "25.x Chapter 25 summary and quiz"
description: "Review of what unsafe means, the five superpowers, raw pointers, safe abstractions, and calling C from Rust and Rust from C."
url: "https://learnrust.net/chapter-25/chapter-25-summary-and-quiz/"
last_updated: "2026-06-13"
---

# 25.x Chapter 25 summary and quiz

The course's most dangerous chapter, taught as a contract rather than an off-switch. Review, then a quiz.

## Quick review

**`unsafe`** doesn't disable Rust's safety checks; it grants exactly **five** extra abilities the compiler can't verify ([25.1](https://learnrust.net/chapter-25/what-unsafe-means/)): dereference a raw pointer, call an unsafe function, access a mutable `static`, implement an unsafe trait, access a union field. Inside an `unsafe` block the borrow checker and everything else stay fully active. Every `unsafe` block gets a `// SAFETY:` comment justifying why it's sound. It exists because the borrow checker is conservative and because talking to the OS/hardware/C requires it, the standard library is built on contained `unsafe`.

**Raw pointers** `*const T` and `*mut T` are addresses without a reference's guarantees ([25.2](https://learnrust.net/chapter-25/raw-pointers/)): they can be null, dangling, or aliased, with no lifetime. *Creating* them is safe (just computing an address); *dereferencing* them needs `unsafe` (that's where invalid memory bites).

**Unsafe functions** carry caller preconditions and need `unsafe` to call ([25.3](https://learnrust.net/chapter-25/unsafe-functions-and-safe-abstractions/)). The core pattern is wrapping a tiny unsafe core in a *safe* abstraction that guarantees the preconditions for all inputs, `split_at_mut` returns two disjoint mutable slices (sound, but unprovable by the checker) behind a safe API. Mark a function safe only if it's truly safe for *every* input.

**FFI** crosses the language boundary. Calling C from Rust uses `extern "C"` declarations, and every foreign call is `unsafe` because Rust can't verify the foreign code ([25.4](https://learnrust.net/chapter-25/calling-c-from-rust/)); data crosses as C types (`CString`, raw pointers). Calling Rust from C uses `#[no_mangle] pub extern "C"` plus a `cdylib` build ([25.5](https://learnrust.net/chapter-25/calling-rust-from-c/)), making Rust callable from any C-speaking language, "Rust in anything", with defensive handling of untrusted foreign inputs.

## Quiz time

**Question #1**

A teammate says "wrap it in `unsafe` to get the borrow checker to stop complaining." Why is that wrong?

<details class="solution">
<summary>Show solution</summary>

Because `unsafe` does *not* turn off the borrow checker, ownership, lifetimes, or type checking, all of those stay fully active inside an `unsafe` block. `unsafe` only adds five specific abilities (dereferencing raw pointers, calling unsafe functions, etc.). A normal borrow error won't go away by wrapping it in `unsafe`; you'd still get the same error. `unsafe` is for operations the compiler *can't verify*, not a way to silence checks it can.

</details>

**Question #2**

Why is creating a raw pointer safe but dereferencing it unsafe?

<details class="solution">
<summary>Show solution</summary>

Creating a raw pointer just computes an address (a number), which reads/writes nothing and can't cause harm, so it's allowed in safe code. Dereferencing actually accesses the memory at that address; if the pointer is null, dangling, or invalid, that's undefined behavior the compiler can't rule out. So the harmless act (making the address) stays safe and the dangerous act (trusting it enough to touch memory) requires `unsafe`.

</details>

**Question #3**

`split_at_mut` returns two mutable references into one slice, which sounds like it breaks chapter 9's rules. Why is it actually safe, and why does it need `unsafe` internally anyway?

<details class="solution">
<summary>Show solution</summary>

It's safe because the two slices cover *disjoint* ranges of the original (`[0, mid)` and `[mid, len)`), so they never point at the same memory, no aliasing, no real violation. It needs `unsafe` internally because the borrow checker can't *see* that the two ranges are disjoint; it only sees two mutable borrows of one slice and refuses. So the implementation uses raw pointers (which ignore the aliasing rules) inside `unsafe`, guarded by an `assert!(mid <= len)`, and wraps it in a safe function the caller uses with no `unsafe`.

</details>

**Question #4**

Why does calling a C function from Rust always require `unsafe`, and why is a Rust function *exposed* to C written in ordinary safe Rust yet still needing defensive coding?

<details class="solution">
<summary>Show solution</summary>

Calling C requires `unsafe` because Rust can't verify the foreign code, that it matches the declared signature, respects ownership, or stays in bounds, so you vouch for it crossing out of the checked zone. A Rust function exposed to C has a normal safe body (exposing it doesn't make its insides unsafe), but its *callers* are C, which Rust can't constrain: C may pass wrong types or invalid pointers. So the function must validate inputs and treat foreign data as untrusted, the danger is at the boundary, not in the Rust implementation.

</details>

You've now seen the one place Rust's safety guarantee has a seam, and how the language contains it so that "unsafe" means a small, audited, well-justified contract rather than a free-for-all. That completes the language. Chapter 26 is the finale: a complete, working, multithreaded web server that puts the whole course together, ownership, traits, error handling, closures, threads, channels, and smart pointers, in one real program, built step by step and compiling at every stage. And, fittingly, it needs no `unsafe` at all.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
