r[type.closure]

Closure types

r[type.closure.intro] A closure expression produces a closure value with a unique, anonymous type that cannot be written out. A closure type is approximately equivalent to a struct which contains the captured values. For instance, the following closure:

#[derive(Debug)]
struct Point { x: i32, y: i32 }
struct Rectangle { left_top: Point, right_bottom: Point }

fn f<F : FnOnce() -> String> (g: F) {
    println!("{}", g());
}

let mut rect = Rectangle {
    left_top: Point { x: 1, y: 1 },
    right_bottom: Point { x: 0, y: 0 }
};

let c = || {
    rect.left_top.x += 1;
    rect.right_bottom.x += 1;
    format!("{:?}", rect.left_top)
};
f(c); // Prints "Point { x: 2, y: 1 }".

generates a closure type roughly like the following:

// Note: This is not exactly how it is translated, this is only for
// illustration.

struct Closure<'a> {
    left_top : &'a mut Point,
    right_bottom_x : &'a mut i32,
}

impl<'a> FnOnce<()> for Closure<'a> {
    type Output = String;
    extern "rust-call" fn call_once(self, args: ()) -> String {
        self.left_top.x += 1;
        *self.right_bottom_x += 1;
        format!("{:?}", self.left_top)
    }
}

so that the call to f works as if it were:

f(Closure{ left_top: &mut rect.left_top, right_bottom_x: &mut rect.right_bottom.x });

r[type.closure.capture]

Capture modes

r[type.closure.capture.intro] A capture mode determines how a place expression from the environment is borrowed or moved into the closure. The capture modes are:

  1. Immutable borrow (ImmBorrow) --- The place expression is captured as a shared reference.
  2. Unique immutable borrow (UniqueImmBorrow) --- This is similar to an immutable borrow, but must be unique as described below.
  3. Mutable borrow (MutBorrow) --- The place expression is captured as a mutable reference.
  4. Move (ByValue) --- The place expression is captured by moving the value into the closure.

r[type.closure.capture.precedence] Place expressions from the environment are captured from the first mode that is compatible with how the captured value is used inside the closure body. The mode is not affected by the code surrounding the closure, such as the lifetimes of involved variables or fields, or of the closure itself.

r[type.closure.capture.copy]

Copy values

Values that implement Copy that are moved into the closure are captured with the ImmBorrow mode.

let x = [0; 1024];
let c = || {
    let y = x; // x captured by ImmBorrow
};

r[type.closure.async.input]

Async input capture

Async closures always capture all input arguments, regardless of whether or not they are used within the body.

Capture precision

r[type.closure.capture.precision.capture-path] A capture path is a sequence starting with a variable from the environment followed by zero or more place projections from that variable.

r[type.closure.capture.precision.place-projection] A place projection is a field access, tuple index, dereference (and automatic dereferences), array or slice index expression, or pattern destructuring applied to a variable.

[!NOTE] In rustc, pattern destructuring desugars into a series of dereferences and field or element accesses.

r[type.closure.capture.precision.intro] The closure borrows or moves the capture path, which may be truncated based on the rules described below.

For example:

struct SomeStruct {
    f1: (i32, i32),
}
let s = SomeStruct { f1: (1, 2) };

let c = || {
    let x = s.f1.1; // s.f1.1 captured by ImmBorrow
};
c();

Here the capture path is the local variable s, followed by a field access .f1, and then a tuple index .1. This closure captures an immutable borrow of s.f1.1.

r[type.closure.capture.precision.shared-prefix]

Shared prefix

In the case where a capture path and one of the ancestors of that path are both captured by a closure, the ancestor path is captured with the highest capture mode among the two captures, CaptureMode = max(AncestorCaptureMode, DescendantCaptureMode), using the strict weak ordering:

ImmBorrow < UniqueImmBorrow < MutBorrow < ByValue

Note that this might need to be applied recursively.

// In this example, there are three different capture paths with a shared ancestor:
# fn move_value<T>(_: T){}
let s = String::from("S");
let t = (s, String::from("T"));
let mut u = (t, String::from("U"));

let c = || {
    println!("{:?}", u); // u captured by ImmBorrow
    u.1.truncate(0); // u.0 captured by MutBorrow
    move_value(u.0.0); // u.0.0 captured by ByValue
};
c();

Overall this closure will capture u by ByValue.

r[type.closure.capture.precision.dereference-shared]

Rightmost shared reference truncation

The capture path is truncated at the rightmost dereference in the capture path if the dereference is applied to a shared reference.

This truncation is allowed because fields that are read through a shared reference will always be read via a shared reference or a copy. This helps reduce the size of the capture when the extra precision does not yield any benefit from a borrow checking perspective.

The reason it is the rightmost dereference is to help avoid a shorter lifetime than is necessary. Consider the following example:

struct Int(i32);
struct B<'a>(&'a i32);

struct MyStruct<'a> {
   a: &'static Int,
   b: B<'a>,
}

fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
    let c = || drop(&m.a.0);
    c
}

If this were to capture m, then the closure would no longer outlive 'static, since m is constrained to 'a. Instead, it captures (*(*m).a) by ImmBorrow.

r[type.closure.capture.precision.wildcard]

Wildcard pattern bindings

r[type.closure.capture.precision.wildcard.reads] Closures only capture data that needs to be read. Binding a value with a wildcard pattern does not read the value, so the place is not captured.

struct S; // A non-`Copy` type.
let x = S;
let c = || {
    let _ = x;  // Does not capture `x`.
};
let c = || match x {
    _ => (), // Does not capture `x`.
};
x; // OK: `x` can be moved here.
c();

r[type.closure.capture.precision.wildcard.destructuring] Destructuring tuples, structs, and single-variant enums does not, by itself, cause a read or the place to be captured.

[!NOTE] Enums marked with [#[non_exhaustive]][attributes.type-system.non_exhaustive] from other crates are always treated as having multiple variants. See [type.closure.capture.precision.discriminants.non_exhaustive].

struct S; // A non-`Copy` type.

// Destructuring tuples does not cause a read or capture.
let x = (S,);
let c = || {
    let (..) = x; // Does not capture `x`.
};
x; // OK: `x` can be moved here.
c();

// Destructuring unit structs does not cause a read or capture.
let x = S;
let c = || {
    let S = x; // Does not capture `x`.
};
x; // OK: `x` can be moved here.
c();

// Destructuring structs does not cause a read or capture.
struct W<T>(T);
let x = W(S);
let c = || {
    let W(..) = x; // Does not capture `x`.
};
x; // OK: `x` can be moved here.
c();

// Destructuring single-variant enums does not cause a read
// or capture.
enum E<T> { V(T) }
let x = E::V(S);
let c = || {
    let E::V(..) = x; // Does not capture `x`.
};
x; // OK: `x` can be moved here.
c();

r[type.closure.capture.precision.wildcard.fields] Fields matched against [RestPattern] (..) or [StructPatternEtCetera] (also ..) are not read, and those fields are not captured.

struct S; // A non-`Copy` type.
let x = (S, S);
let c = || {
    let (x0, ..) = x;  // Captures `x.0` by `ByValue`.
};
// Only the first tuple field was captured by the closure.
x.1; // OK: `x.1` can be moved here.
c();

r[type.closure.capture.precision.wildcard.array-slice] Partial captures of arrays and slices are not supported; the entire slice or array is always captured even if used with wildcard pattern matching, indexing, or sub-slicing.

struct S; // A non-`Copy` type.
let mut x = [S, S];
let c = || {
    let [x0, _] = x; // Captures all of `x` by `ByValue`.
};
let _ = &mut x[1]; // ERROR: Borrow of moved value.

r[type.closure.capture.precision.wildcard.initialized] Values that are matched with wildcards must still be initialized.

let x: u8;
let c = || {
    let _ = x; // ERROR: Binding `x` isn't initialized.
};

r[type.closure.capture.precision.discriminants]

Capturing for discriminant reads

r[type.closure.capture.precision.discriminants.reads] If pattern matching reads a discriminant, the place containing that discriminant is captured by ImmBorrow.

r[type.closure.capture.precision.discriminants.multiple-variant] Matching against a variant of an enum that has more than one variant reads the discriminant, capturing the place by ImmBorrow.

struct S; // A non-`Copy` type.
let mut x = (Some(S), S);
let c = || match x {
    (None, _) => (),
//   ^^^^
// This pattern requires reading the discriminant, which
// causes `x.0` to be captured by `ImmBorrow`.
    _ => (),
};
let _ = &mut x.0; // ERROR: Cannot borrow `x.0` as mutable.
//           ^^^
// The closure is still live, so `x.0` is still immutably
// borrowed here.
c();
# struct S; // A non-`Copy` type.
# let x = (Some(S), S);
let c = || match x { // Captures `x.0` by `ImmBorrow`.
    (None, _) => (),
    _ => (),
};
// Though `x.0` is captured due to the discriminant read,
// `x.1` is not captured.
x.1; // OK: `x.1` can be moved here.
c();

r[type.closure.capture.precision.discriminants.single-variant] Matching against the only variant of a single-variant enum does not read the discriminant and does not capture the place.

enum E<T> { V(T) } // A single-variant enum.
let x = E::V(());
let c = || {
    let E::V(_) = x; // Does not capture `x`.
};
x; // OK: `x` can be moved here.
c();

r[type.closure.capture.precision.discriminants.non_exhaustive] If [#[non_exhaustive]][attributes.type-system.non_exhaustive] is applied to an enum defined in an external crate, the enum is treated as having multiple variants for the purpose of deciding whether a read occurs, even if it actually has only one variant.

r[type.closure.capture.precision.discriminants.uninhabited-variants] Even if all variants but the one being matched against are uninhabited, making the pattern [irrefutable][patterns.refutable], the discriminant is still read if it otherwise would be.

enum Empty {}
let mut x = Ok::<_, Empty>(42);
let c = || {
    let Ok(_) = x; // Captures `x` by `ImmBorrow`.
};
let _ = &mut x; // ERROR: Cannot borrow `x` as mutable.
c();

r[type.closure.capture.precision.range-patterns]

Capturing and range patterns

r[type.closure.capture.precision.range-patterns.reads] Matching against a [range pattern][patterns.range] reads the place being matched, even if the range includes all possible values of the type, and captures the place by ImmBorrow.

let mut x = 0u8;
let c = || {
    let 0..=u8::MAX = x; // Captures `x` by `ImmBorrow`.
};
let _ = &mut x; // ERROR: Cannot borrow `x` as mutable.
c();

r[type.closure.capture.precision.slice-patterns]

Capturing and slice patterns

r[type.closure.capture.precision.slice-patterns.slices] Matching a slice against a [slice pattern][patterns.slice] other than one with only a single [rest pattern][patterns.rest] (i.e. [..]) is treated as a read of the length from the slice and captures the slice by ImmBorrow.

let x: &mut [u8] = &mut [];
let c = || match x { // Captures `*x` by `ImmBorrow`.
    &mut [] => (),
//       ^^
// This matches a slice of exactly zero elements. To know whether the
// scrutinee matches, the length must be read, causing the slice to
// be captured.
    _ => (),
};
let _ = &mut *x; // ERROR: Cannot borrow `*x` as mutable.
c();
let x: &mut [u8] = &mut [];
let c = || match x { // Does not capture `*x`.
    [..] => (),
//   ^^ Rest pattern.
};
let _ = &mut *x; // OK: `*x` can be borrow here.
c();

[!NOTE] Perhaps surprisingly, even though the length is contained in the (wide) pointer to the slice, it is the place of the pointee (the slice) that is treated as read and is captured.

fn f<'l: 's, 's>(x: &'s mut &'l [u8]) -> impl Fn() + 'l {
    // The closure outlives `'l` because it captures `**x`. If
    // instead it captured `*x`, it would not live long enough
    // to satisfy the `impl Fn() + 'l` bound.
    || match *x { // Captures `**x` by `ImmBorrow`.
        &[] => (),
        _ => (),
    }
}

In this way, the behavior is consistent with dereferencing to the slice in the scrutinee.

fn f<'l: 's, 's>(x: &'s mut &'l [u8]) -> impl Fn() + 'l {
    || match **x { // Captures `**x` by `ImmBorrow`.
        [] => (),
        _ => (),
    }
}

For details, see Rust PR #138961.

r[type.closure.capture.precision.slice-patterns.arrays] As the length of an array is fixed by its type, matching an array against a slice pattern does not by itself capture the place.

let x: [u8; 1] = [0];
let c = || match x { // Does not capture `x`.
    [_] => (), // Length is fixed.
};
x; // OK: `x` can be moved here.
c();

r[type.closure.capture.precision.move-dereference]

Capturing references in move contexts

Because it is not allowed to move fields out of a reference, move closures will only capture the prefix of a capture path that runs up to, but not including, the first dereference of a reference. The reference itself will be moved into the closure.

struct T(String, String);

let mut t = T(String::from("foo"), String::from("bar"));
let t_mut_ref = &mut t;
let mut c = move || {
    t_mut_ref.0.push_str("123"); // captures `t_mut_ref` ByValue
};
c();

r[type.closure.capture.precision.raw-pointer-dereference]

Raw pointer dereference

Because it is unsafe to dereference a raw pointer, closures will only capture the prefix of a capture path that runs up to, but not including, the first dereference of a raw pointer.

struct T(String, String);

let t = T(String::from("foo"), String::from("bar"));
let t_ptr = &t as *const T;

let c = || unsafe {
    println!("{}", (*t_ptr).0); // captures `t_ptr` by ImmBorrow
};
c();

r[type.closure.capture.precision.union]

Union fields

Because it is unsafe to access a union field, closures will only capture the prefix of a capture path that runs up to the union itself.

union U {
    a: (i32, i32),
    b: bool,
}
let u = U { a: (123, 456) };

let c = || {
    let x = unsafe { u.a.0 }; // captures `u` ByValue
};
c();

// This also includes writing to fields.
let mut u = U { a: (123, 456) };

let mut c = || {
    u.b = true; // captures `u` with MutBorrow
};
c();

r[type.closure.capture.precision.unaligned]

Reference into unaligned structs

Because it is undefined behavior to create references to unaligned fields in a structure, closures will only capture the prefix of the capture path that runs up to, but not including, the first field access into a structure that uses the packed representation. This includes all fields, even those that are aligned, to protect against compatibility concerns should any of the fields in the structure change in the future.

#[repr(packed)]
struct T(i32, i32);

let t = T(2, 5);
let c = || {
    let a = t.0; // captures `t` with ImmBorrow
};
// Copies out of `t` are ok.
let (a, b) = (t.0, t.1);
c();

Similarly, taking the address of an unaligned field also captures the entire struct:

#[repr(packed)]
struct T(String, String);

let mut t = T(String::new(), String::new());
let c = || {
    let a = std::ptr::addr_of!(t.1); // captures `t` with ImmBorrow
};
let a = t.0; // ERROR: cannot move out of `t.0` because it is borrowed
c();

but the above works if it is not packed since it captures the field precisely:

struct T(String, String);

let mut t = T(String::new(), String::new());
let c = || {
    let a = std::ptr::addr_of!(t.1); // captures `t.1` with ImmBorrow
};
// The move here is allowed.
let a = t.0;
c();

r[type.closure.capture.precision.box-deref]

Box vs other Deref implementations

The implementation of the Deref trait for Box is treated differently from other Deref implementations, as it is considered a special entity.

For example, let us look at examples involving Rc and Box. The *rc is desugared to a call to the trait method deref defined on Rc, but since *box is treated differently, it is possible to do a precise capture of the contents of the Box.

r[type.closure.capture.precision.box-non-move.not-moved]

Box with non-move closure

In a non-move closure, if the contents of the Box are not moved into the closure body, the contents of the Box are precisely captured.

struct S(String);

let b = Box::new(S(String::new()));
let c_box = || {
    let x = &(*b).0; // captures `(*b).0` by ImmBorrow
};
c_box();

// Contrast `Box` with another type that implements Deref:
let r = std::rc::Rc::new(S(String::new()));
let c_rc = || {
    let x = &(*r).0; // captures `r` by ImmBorrow
};
c_rc();

r[type.closure.capture.precision.box-non-move.moved] However, if the contents of the Box are moved into the closure, then the box is entirely captured. This is done so the amount of data that needs to be moved into the closure is minimized.

// This is the same as the example above except the closure
// moves the value instead of taking a reference to it.

struct S(String);

let b = Box::new(S(String::new()));
let c_box = || {
    let x = (*b).0; // captures `b` with ByValue
};
c_box();

r[type.closure.capture.precision.box-move.read]

Box with move closure

Similarly to moving contents of a Box in a non-move closure, reading the contents of a Box in a move closure will capture the Box entirely.

struct S(i32);

let b = Box::new(S(10));
let c_box = move || {
    let x = (*b).0; // captures `b` with ByValue
};

r[type.closure.unique-immutable]

Unique immutable borrows in captures

Captures can occur by a special kind of borrow called a unique immutable borrow, which cannot be used anywhere else in the language and cannot be written out explicitly. It occurs when modifying the referent of a mutable reference, as in the following example:

let mut b = false;
let x = &mut b;
let mut c = || {
    // An ImmBorrow and a MutBorrow of `x`.
    let a = &x;
    *x = true; // `x` captured by UniqueImmBorrow
};
// The following line is an error:
// let y = &x;
c();
// However, the following is OK.
let z = &x;

In this case, borrowing x mutably is not possible, because x is not mut. But at the same time, borrowing x immutably would make the assignment illegal, because a & &mut reference might not be unique, so it cannot safely be used to modify a value. So a unique immutable borrow is used: it borrows x immutably, but like a mutable borrow, it must be unique.

In the above example, uncommenting the declaration of y will produce an error because it would violate the uniqueness of the closure‘s borrow of x; the declaration of z is valid because the closure’s lifetime has expired at the end of the block, releasing the borrow.

r[type.closure.call]

Call traits and coercions

r[type.closure.call.intro] Closure types all implement [FnOnce], indicating that they can be called once by consuming ownership of the closure. Additionally, some closures implement more specific call traits:

r[type.closure.call.fn-mut]

  • A closure which does not move out of any captured variables implements [FnMut], indicating that it can be called by mutable reference.

r[type.closure.call.fn]

  • A closure which does not mutate or move out of any captured variables implements [Fn], indicating that it can be called by shared reference.

[!NOTE] move closures may still implement [Fn] or [FnMut], even though they capture variables by move. This is because the traits implemented by a closure type are determined by what the closure does with captured values, not how it captures them.

r[type.closure.non-capturing] Non-capturing closures are closures that don't capture anything from their environment. Non-async, non-capturing closures can be coerced to function pointers (e.g., fn()) with the matching signature.

let add = |x, y| x + y;

let mut x = add(5,7);

type Binop = fn(i32, i32) -> i32;
let bo: Binop = add;
x = bo(5,7);

r[type.closure.async.traits]

Async closure traits

r[type.closure.async.traits.fn-family] Async closures have a further restriction of whether or not they implement [FnMut] or [Fn].

The [Future] returned by the async closure has similar capturing characteristics as a closure. It captures place expressions from the async closure based on how they are used. The async closure is said to be lending to its [Future] if it has either of the following properties:

  • The Future includes a mutable capture.
  • The async closure captures by value, except when the value is accessed with a dereference projection.

If the async closure is lending to its Future, then [FnMut] and [Fn] are not implemented. [FnOnce] is always implemented.

Example: The first clause for a mutable capture can be illustrated with the following:

fn takes_callback<Fut: Future>(c: impl FnMut() -> Fut) {}

fn f() {
    let mut x = 1i32;
    let c = async || {
        x = 2;  // x captured with MutBorrow
    };
    takes_callback(c);  // ERROR: async closure does not implement `FnMut`
}

The second clause for a regular value capture can be illustrated with the following:

fn takes_callback<Fut: Future>(c: impl Fn() -> Fut) {}

fn f() {
    let x = &1i32;
    let c = async move || {
        let a = x + 2;  // x captured ByValue
    };
    takes_callback(c);  // ERROR: async closure does not implement `Fn`
}

The exception of the the second clause can be illustrated by using a dereference, which does allow Fn and FnMut to be implemented:

fn takes_callback<Fut: Future>(c: impl Fn() -> Fut) {}

fn f() {
    let x = &1i32;
    let c = async move || {
        let a = *x + 2;
    };
    takes_callback(c);  // OK: implements `Fn`
}

r[type.closure.async.traits.async-family] Async closures implement [AsyncFn], [AsyncFnMut], and [AsyncFnOnce] in an analogous way as regular closures implement [Fn], [FnMut], and [FnOnce]; that is, depending on the use of the captured variables in its body.

r[type.closure.traits]

Other traits

r[type.closure.traits.intro] All closure types implement Sized. Additionally, closure types implement the following traits if allowed to do so by the types of the captures it stores:

r[type.closure.traits.behavior] The rules for Send and Sync match those for normal struct types, while Clone and Copy behave as if derived. For Clone, the order of cloning of the captured values is left unspecified.

Because captures are often by reference, the following general rules arise:

  • A closure is Sync if all captured values are Sync.
  • A closure is Send if all values captured by non-unique immutable reference are Sync, and all values captured by unique immutable or mutable reference, copy, or move are Send.
  • A closure is Clone or Copy if it does not capture any values by unique immutable or mutable reference, and if all values it captures by copy or move are Clone or Copy, respectively.

r[type.closure.drop-order]

Drop order

If a closure captures a field of a composite types such as structs, tuples, and enums by value, the field's lifetime would now be tied to the closure. As a result, it is possible for disjoint fields of a composite types to be dropped at different times.

{
    let tuple =
      (String::from("foo"), String::from("bar")); // --+
    { //                                               |
        let c = || { // ----------------------------+  |
            // tuple.0 is captured into the closure |  |
            drop(tuple.0); //                       |  |
        }; //                                       |  |
    } // 'c' and 'tuple.0' dropped here ------------+  |
} // tuple.1 dropped here -----------------------------+

r[type.closure.capture.precision.edition2018.entirety]

Edition 2018 and before

Closure types difference

In Edition 2018 and before, closures always capture a variable in its entirety, without its precise capture path. This means that for the example used in the Closure types section, the generated closure type would instead look something like this:

struct Closure<'a> {
    rect : &'a mut Rectangle,
}

impl<'a> FnOnce<()> for Closure<'a> {
    type Output = String;
    extern "rust-call" fn call_once(self, args: ()) -> String {
        self.rect.left_top.x += 1;
        self.rect.right_bottom.x += 1;
        format!("{:?}", self.rect.left_top)
    }
}

and the call to f would work as follows:

f(Closure { rect: rect });

r[type.closure.capture.precision.edition2018.composite]

Capture precision difference

Composite types such as structs, tuples, and enums are always captured in its entirety, not by individual fields. As a result, it may be necessary to borrow into a local variable in order to capture a single field:

# use std::collections::HashSet;
#
struct SetVec {
    set: HashSet<u32>,
    vec: Vec<u32>
}

impl SetVec {
    fn populate(&mut self) {
        let vec = &mut self.vec;
        self.set.iter().for_each(|&n| {
            vec.push(n);
        })
    }
}

If, instead, the closure were to use self.vec directly, then it would attempt to capture self by mutable reference. But since self.set is already borrowed to iterate over, the code would not compile.

r[type.closure.capture.precision.edition2018.move] If the move keyword is used, then all captures are by move or, for Copy types, by copy, regardless of whether a borrow would work. The move keyword is usually used to allow the closure to outlive the captured values, such as if the closure is being returned or used to spawn a new thread.

r[type.closure.capture.precision.edition2018.wildcard] Regardless of if the data will be read by the closure, i.e. in case of wild card patterns, if a variable defined outside the closure is mentioned within the closure the variable will be captured in its entirety.

r[type.closure.capture.precision.edition2018.drop-order]

Drop order difference

As composite types are captured in their entirety, a closure which captures one of those composite types by value would drop the entire captured variable at the same time as the closure gets dropped.

{
    let tuple =
      (String::from("foo"), String::from("bar"));
    {
        let c = || { // --------------------------+
            // tuple is captured into the closure |
            drop(tuple.0); //                     |
        }; //                                     |
    } // 'c' and 'tuple' dropped here ------------+
}