---
title: "19.1 Closures"
description: "Closures are functions you can write inline and that capture variables from the surrounding scope, by reference or by move."
url: "https://learnrust.net/chapter-19/closures/"
last_updated: "2026-06-13"
---

# 19.1 Closures

You've written a lot of functions. Every one of them has been a named, top-level thing: `fn main`, `fn largest`, a method in an `impl` block. A function written that way can only see what you hand it through its parameters; it knows nothing about the code around its definition, because there is no "around", it sits at the top level on its own. A **closure** is a function you can write inline, right where you use it, and that can see the variables in the scope where you wrote it. That second half is the whole point, and it's where the name comes from: the closure *closes over* the variables it uses.

This chapter is, in a sense, where Rust starts paying you back. Closures and iterators are the tools that turn the loops you've been writing by hand into short, declarative pipelines, and almost every convenient method in the standard library takes a closure. We'll start with closures alone, then spend the rest of the chapter on the iterators that make them sing.

## A first closure

Here's a closure that adds one to a number, stored in a variable and then called:

```rust
fn main() {
    let add_one = |x| x + 1;
    let result = add_one(5);
    println!("{result}");
}
```

```
6
```

The `|x| x + 1` is the closure. The parameters go between the vertical bars (here just `x`), and the body comes after. We call it exactly like a function: `add_one(5)`. If the body needs more than one expression, wrap it in braces:

```rust
let describe = |x| {
    let doubled = x * 2;
    format!("{x} doubled is {doubled}")
};
println!("{}", describe(4));
```

```
4 doubled is 8
```

Compare a closure to the equivalent function and the family resemblance is clear:

```rust
fn  add_one_fn(x: i32) -> i32 { x + 1 }   // a function
let add_one_cl = |x: i32| -> i32 { x + 1 };   // the same, as a closure
```

The closure syntax is lighter: the bars instead of parentheses, and crucially, the type annotations are optional. A top-level `fn` must spell out every parameter and return type, because it's a public contract that anyone might call. A closure is usually a throwaway used right here, so the compiler infers its types from how it's called, the same inference you've relied on for `let` since chapter 1. You *can* annotate a closure (the `add_one_cl` line above does), and occasionally you'll have to, but most closures you write will have none.

> **A closure's types are inferred once, then fixed**
>
> Because a closure's types are inferred from use, the *first* call pins them down. After that, the closure has one concrete signature like any function:
>
> ```rust
> let identity = |x| x;
> let s = identity(String::from("hi"));   // x is now String, forever
> let n = identity(5);                     // error: expected String, found integer
> ```
>
> The compiler reports `expected struct String, found integer` on the second call. A closure isn't generic; it just lets you skip writing the types it does have. If you need it to work for many types, that's a generic function (chapter 15), not a closure.

## Capturing the environment

Inline syntax is a nicety. Capturing is the feature. A closure can use variables that are in scope where it's defined, not just its own parameters:

```rust
fn main() {
    let threshold = 10;
    let is_big = |x| x > threshold;

    println!("{}", is_big(15));
    println!("{}", is_big(3));
}
```

```
true
false
```

`is_big` takes one parameter, `x`, but it also reads `threshold`, a variable from `main`. It captured it. A plain `fn` can't do this; if you tried to write `fn is_big(x: i32) -> bool { x > threshold }`, you'd get an error that `threshold` isn't in scope, because a function body can only see its parameters and globals. The closure can see `threshold` because it was *defined* in the scope where `threshold` lives.

This is why closures matter for the rest of the chapter. When you tell a list to "keep every element greater than `threshold`", you hand it a closure, and the closure carries `threshold` along with it. The behavior and the data it needs travel together as one value.

## Capturing by reference

The natural question after chapter 8: when a closure captures `threshold`, does it *take* it? By default, a closure captures by the least invasive means that still works. If it only reads a captured value, it captures a shared reference, exactly the `&` borrow from chapter 9:

```rust
fn main() {
    let greeting = String::from("hello");
    let print_it = || println!("{greeting}");

    print_it();
    print_it();
    println!("{greeting} is still usable here");
}
```

```
hello
hello
hello is still usable here
```

The closure `print_it` only reads `greeting`, so it borrows it with `&`. That's a shared borrow, so we can call the closure twice and still use `greeting` afterward, all the chapter-9 rules apply unchanged. (Note `print_it` has empty bars `||`: it takes no parameters of its own, it works entirely off the captured `greeting`.)

If the closure *mutates* a captured value, it captures a mutable reference `&mut` instead, and then the closure variable itself must be `mut`:

```rust
fn main() {
    let mut count = 0;
    let mut increment = || count += 1;

    increment();
    increment();
    println!("{count}");
}
```

```
2
```

`increment` holds a `&mut count` for as long as it's alive. While that mutable borrow exists you can't also read `count` (the chapter-9 rule again), which is why the `println!` waits until after the last call. The compiler chooses `&`, `&mut`, or, as we'll see next lesson, a move, based on what the closure's body actually does. You don't ask; it figures out the lightest capture that compiles.

## Capturing by move

Sometimes borrowing isn't enough: you want the closure to *own* what it captures. The `move` keyword forces every capture to be taken by value:

```rust
fn main() {
    let name = String::from("Ada");
    let greet = move || println!("Hi, {name}");

    greet();
    // name has been moved into the closure; it's gone here.
}
```

```
Hi, Ada
```

With `move`, `name` is moved into `greet`, so `main` can no longer use `name` afterward (it's been moved, chapter 8). Why would you want that? Because a borrowing closure can't outlive what it borrows. The moment a closure needs to travel somewhere the original variable won't follow, to another thread (chapter 22), or returned out of the function that made it, it has to own its captures, and `move` is how you say so. We'll meet the returning case in the next lesson and the thread case in chapter 22; for now, the rule is simply that `move` makes the closure take ownership.

> **Key insight**
>
> A closure bundles two things: code to run, and the captured environment it needs to run. The capture mode, `&`, `&mut`, or owned, is just the chapter-8-and-9 ownership system applied to that bundle. There's no new memory model here. A closure is data (its captures) plus behavior, and Rust borrows or moves that data by exactly the rules you already know.

## Quiz time

**Question #1**

What can a closure do that a plain `fn` cannot?

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

Capture variables from the scope where it's defined. A `fn` can only use its parameters and globals; a closure can also read or modify the surrounding local variables (it "closes over" them). It can also be written inline with inferred types, but capturing is the defining capability.

</details>

**Question #2**

By default, does a closure capture a variable by reference or by value? When does `move` change that?

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

By default a closure captures by the least invasive means that compiles: a shared reference `&` if it only reads the value, a mutable reference `&mut` if it modifies it, and by value only if the body requires ownership. The `move` keyword forces *every* capture to be by value (owned), which you need when the closure must outlive the original variable, for example to send it to another thread (chapter 22) or return it from a function.

</details>

**Question #3**

Will this compile? If not, why?

```rust
let mut total = 0;
let add = |x| total += x;
println!("{total}");
add(5);
```

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

No. `add` mutates `total`, so it captures `&mut total` and must itself be declared `mut` (`let mut add = ...`). On top of that, the `println!` tries to read `total` while `add` still holds a mutable borrow of it, which is the E0502 conflict from chapter 9. Two fixes are needed: make `add` mutable, and move the `println!` to after the last call to `add`.

</details>

Closures capture their environment, but to pass one to another function, or store it, or return it, the compiler needs to talk about a closure's *type*. That's the job of the `Fn` traits, and the next lesson ([19.2](https://learnrust.net/chapter-19/closures-and-the-fn-traits/)) introduces them.

## Sitemap

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