Note: None of this is implemented.
A closure is a function that preserves its state from one invocation to the next. The closure's logic accesses its state using bound closure variables.
Because closures preserve their state, they are useful for:
- Iterators, which retrieve a sequence of values one-at-a-time from a defined collection.
- Generators that return a different value on each use
- Event-triggered callbacks, whose state guides what happens after an event is triggered.
Simple Closure
A closure definition looks similar to a function definition, but uses vertical bars to enclose parameters. For example:
imm cnt = 5 mut counter = || cnt++ // closure definition counter() // 5 counter() // 6
In this case, the closure takes no parameters. It infers that the return value is an integer. Importantly, the closure's state is established by the variable it binds from the outer scope, in this case the local variable cnt. Each time the closure is called, it increments the value held by cnt. which is why it returns a different value each time it is called.
A closure's state is bound by value, which means it is created using a copy of the bound variable(s). Any change to cnt within the closure has no impact on the outer scope's cnt variable. (Binding state by reference is also possible, but must be always done explicitly such that the value is a reference.)
Explicit State Definition
Sometime, it easier (or clearer) to define a closure's state explicitly. For example:
mut counter = |n i32 {cnt = 0}| cnt += n counter(1) // 1 counter(2) // 3
This closure's state is explicitly defined within curly braces at the end of the parameter list. Each closure variable should specify an initial value. Multiple closure variables are delimited by semi-colons.
It is often better to define an explicit state. It signals to the code reader which variables represent the closure's state. It simplifies the code (like here) when the closure variable is not a mirror of an existing variable in an outer scope, but can be initialized with some expression or literal. It is also useful when we want to bind by reference.
Closures are values
This page offers a simplistic first-glance at closures. Later pages add important details on the use of closures:
- A closure is syntactic sugar for an anonymous struct. Knowing its underlying type makes it possible to pass around a closure (and its state) just like any other value.
- Like any value, a closure may be allocated on the heap.
- A closure may be passed as a closure reference to another function, allowing it to be used by that function without regard for the shape of its state.