---
title: "25.2 Raw pointers"
description: "The *const T and *mut T raw pointers: how they differ from references, creating them safely, and why dereferencing them requires unsafe."
url: "https://learnrust.net/chapter-25/raw-pointers/"
last_updated: "2026-06-13"
---

# 25.2 Raw pointers

The first of `unsafe`'s five powers (lesson [25.1](https://learnrust.net/chapter-25/what-unsafe-means/)) is dereferencing a **raw pointer**, and the other powers build on it, so it's where the chapter gets concrete. A raw pointer is the lowest-level pointer Rust has: just a memory address, with none of the guarantees that references carry. You've used references (`&T`, `&mut T`) since chapter 9, and the borrow checker has kept them airtight. Raw pointers are references with the safety rails removed, which is exactly why creating one is fine but *following* one needs `unsafe`.

## Two raw pointer types

Raw pointers come in two kinds, mirroring references:

`*const T` is a raw pointer to a `T` you intend to read (like `&T`).

`*mut T` is a raw pointer to a `T` you intend to modify (like `&mut T`).

(Read `*const T` as a single type name, "raw const pointer to `T`"; the `*` here is part of the type, not the dereference operator.) The difference from references isn't the syntax, it's everything the compiler *stops promising*. A raw pointer:

- is allowed to be **null** (point at nothing), which a reference never is;
- is allowed to **dangle** (point at freed or invalid memory), which the borrow checker forbids for references;
- ignores the **borrowing rules** entirely, you can have many `*mut T` to the same data at once, with no aliasing checks;
- has **no lifetime**, the compiler tracks nothing about how long it stays valid.

Every one of those is a guarantee references give you and raw pointers throw away. That's the trade: raw pointers can express things references can't (a nullable pointer for C interop, multiple mutable views the borrow checker would reject), at the cost of the compiler no longer protecting you.

## Creating raw pointers is safe

Here's the surprising part: you can *create* raw pointers in completely ordinary, safe code. No `unsafe` needed. You make them with the `as` cast or the `&raw` operators, most commonly by casting a reference:

```rust
fn main() {
    let mut num = 5;

    let r1 = &num as *const i32;       // a *const i32 (read-only raw pointer)
    let r2 = &mut num as *mut i32;     // a *mut i32 (mutable raw pointer)

    println!("created two raw pointers");
}
```

```
created two raw pointers
```

No `unsafe` anywhere, and this compiles fine. Why is *making* a raw pointer safe? Because a pointer is just a number, an address, and computing an address can't, by itself, cause any harm. Nothing has been read or written. Note also that `r1` and `r2` point at the *same* `num`, a `*const` and a `*mut` to one value coexisting, which the borrow checker would never allow for `&num` and `&mut num`. Raw pointers don't play by those rules, so even creating both is allowed.

## Dereferencing requires unsafe

The line is crossed the moment you *use* a raw pointer to actually touch memory. Following a raw pointer, reading or writing through it with `*`, is one of the five superpowers, and it requires an `unsafe` block:

```rust
fn main() {
    let mut num = 5;

    let r1 = &num as *const i32;
    let r2 = &mut num as *mut i32;

    unsafe {
        println!("r1 points at: {}", *r1);   // read through raw pointer
        *r2 = 10;                            // write through raw pointer
        println!("r1 now points at: {}", *r1);
    }
}
```

```
r1 points at: 5
r1 now points at: 10
```

The `*r1` read and the `*r2 = 10` write must be inside `unsafe`, because *this* is where things could go wrong. If `r1` were null, or pointed at freed memory, dereferencing it would be undefined behavior, a crash or worse, and the compiler can no longer prove it won't. By writing `unsafe`, you're vouching that the pointer really is valid to dereference here (and the `// SAFETY:` comment from last lesson would record why). The split is deliberate and clean: making the address is harmless and stays safe; *trusting* the address enough to read or write through it is the dangerous act, and that's what `unsafe` gates.

> **The compiler stops helping at the dereference**
>
> With references, the borrow checker guarantees that every `*r` touches valid, correctly-borrowed memory. With raw pointers, it guarantees *nothing* at the dereference: a raw pointer can be null, dangling, or aliased in ways that would be instant undefined behavior to follow, and the compiler won't warn you. Dereferencing an invalid raw pointer is exactly the use-after-free / null-deref class of bug that safe Rust eliminated, now back on the table because you asked for it. That's why this single operation needs `unsafe` and a written justification: you've taken over the job the borrow checker was doing.

## Why use them at all?

Given all that danger, why do raw pointers exist? Two honest reasons, and both come up later in this chapter. First, **interfacing with C** (lessons [25.4](https://learnrust.net/chapter-25/calling-c-from-rust/) and [25.5](https://learnrust.net/chapter-25/calling-rust-from-c/)): C has no concept of Rust's references, it speaks in raw addresses that can be null, so any data crossing the language boundary travels as raw pointers. Second, **building safe abstractions** the borrow checker can't see the safety of (next lesson): a few data structures need raw pointers internally to express patterns like two views into different halves of one array, then wrap them so callers stay in safe Rust. Outside those cases, you should essentially never reach for a raw pointer; a reference is what you want. Raw pointers are a tool for the boundary and the library author, not for everyday code.

## Quiz time

**Question #1**

Name two guarantees a reference (`&T`) gives that a raw pointer (`*const T`) does not.

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

Any two of: a reference is never null (a raw pointer can be); a reference never dangles/points at invalid memory (a raw pointer can); references obey the borrowing rules with aliasing checks (raw pointers ignore them, you can have many `*mut` to the same data); a reference has a tracked lifetime (a raw pointer has none). Raw pointers drop all of these in exchange for flexibility, which is why the compiler can't protect you when you use one.

</details>

**Question #2**

Why is *creating* a raw pointer safe, but *dereferencing* it requires `unsafe`?

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

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

</details>

**Question #3**

Give one legitimate reason to use raw pointers, given how dangerous they are.

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

Either: interfacing with C/other languages (lessons 25.4–25.5), which speak in raw addresses (possibly null) and have no notion of Rust references, so boundary data travels as raw pointers; or building a safe abstraction whose internal implementation needs a pattern the borrow checker can't verify (e.g. two mutable views into disjoint parts of one array), then wrapping the raw-pointer code in a safe API. Outside the language boundary and library-internals, you should use references, not raw pointers.

</details>

A raw pointer is dangerous on its own, but the Rust way is to confine that danger and hand callers something safe. The next lesson ([25.3](https://learnrust.net/chapter-25/unsafe-functions-and-safe-abstractions/)) shows how: `unsafe` functions, and the crucial pattern of wrapping unsafe code in a safe abstraction, with `split_at_mut` as the canonical example.

## Sitemap

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