rokdocs
v0.1

4.5. Closures

A closure is a function value without a name that appears in expression position. It can capture variables from the outer environment, which makes it convenient for small functions that depend on surrounding state.

4.5.1. Form

closure_expr   ::= capture_list? closure_params return_type? closure_body
closure_params ::= "||"
                 | "|" closure_param_list "|"
closure_param_list ::= closure_param ("," closure_param)* ","?
closure_param ::= identifier (":" type_expr)?
closure_body   ::= expr | block
capture_list   ::= "[" capture_item ("," capture_item)* "]"

The zero-parameter form || is a single token (see punctuation and operator tokens) and is not split into two |.

|params| body

Parameters are enclosed by left and right | tokens (punctuation and operator tokens). The body is an expression or a block.

Single-Expression body

|x| x + 1
|x: i32| x * 2
|a, b| a + b

A single expression follows |...| directly, and its value is the closure's return value.

Block body

|x: i32| -> i32 {
    let y = x * 2;
    y + 1
}

With a block body, the return type may be given with -> (if omitted, it is inferred).

Zero-Parameter form

|| body
|| { body }

With no parameters, use || (two consecutive pipes). For tokenization, || is defined as one token in the fixed token table of punctuation and operator tokens.

4.5.2. Parameter types

Parameter types may be written or omitted.

|x: i32| x * 2                    // explicit
|x| x * 2                          // inferred

Inference is determined by the context in which the closure is used, for example the function type of the argument position it is passed to or the type of a variable declaration. Without context, subsequent calls determine the parameter type (x: i32 in let f = |x| x * 2; f(4);, 2.10.2).

let f: fn(i32) -> i32 = |x| x * 2;   // x: i32 inferred from context
list.map(|x| x * 2);                  // inferred from map's signature

4.5.3. Inferred or explicit return type

|x| x * 2                          // inferred
|x: i32| -> i32 { x * 2 }          // explicit - only with a block body

An explicit return type (-> type) is used only with a block body. A single-expression body always relies on inference.

4.5.4. Captures

A closure can capture variables from outer scopes.

let n = 10;
let f = |x| x + n;                // captures n
f(5);                              // 15

Capture semantics (value capture by default, with capture lists like [ref x]) are defined in detail in Capture Semantics.

4.5.5. Closures vs functions

Aspectfn named function| ... | closure
Nameyesno
Definition positionmodule or implany expression position
Captures outer variablesnoyes
Callname(...)value(...)
Typefn(...) -> ...same

Both are expressed with the same function type and are interface-compatible, so either fits the same position.

4.5.6. Using closure values

// assigned to a variable
let f = |x| x * 2;

// function argument
list.map(|x| x * 2);

// function return
fn make_adder(n: i32) -> fn(i32) -> i32 {
    |x| x + n
}

// collection
let handlers: List<fn(i32) -> i32> = [|x| x + 1, |x| x - 1, |x| x * 2];

4.5.7. Calls

let f = |x| x * 2;
let r = f(5);                     // same form as an ordinary function call

A closure is called as f(args). On a call, the captured environment and the arguments are used together to evaluate the body.

4.5.8. Immediate invocation: IIFE

A closure may be called immediately after it is defined.

let r = (|x: i32| -> i32 { x * 2 })(5);     // 10

This is uncommon, but it is one way to express a local scope with immediate evaluation. In most cases an ordinary block from block expressions is more natural.

4.5.9. Readability: single expression vs block

list.map(|x| x * 2)               // single expression - short and clear
list.map(|x| { let y = x * 2; y + 1 })   // block - multi-step processing

Choose the form that suits the body's length. The specification fully supports both forms.

4.5.10. Trailing closures

When the last argument of a function call is a closure, it may be moved outside the parentheses for readability. Details are defined in Trailing Closures.

4.5.11. Special variants: async and yield modifiers

Adding the async or yield modifier to a closure creates an asynchronous or generator closure.

async || { ... }                  // closure returning a Future
yield || { ... }                  // closure returning an Iterator
async yield || { ... }            // closure returning a Stream

The detailed rules are defined in Generators, Async and Streams.

4.5.12. Zero-Argument closure shorthand: { body }

In certain contexts, { body } is recognized as a zero-parameter closure shorthand. This shorthand is often used together with trailing closures.

thread::spawn { heavy_work() }    // == thread::spawn(|| { heavy_work() })

The exact rules of this shorthand are defined in Trailing Closures.

Edit this page on GitHub