Note: None of this is implemented.
A closure may be viewed as a function that preserves its state from one invocation to the next. 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.
Closure Definition
A closure definition resembles a function definition. There are three key differences: the name is omitted, the return value type(s) are be omitted (and inferred), and the initial value of the preserved state is specified,
For example:
mut counter = fn(n i32) [cnt = 0]: cnt += n // Closure use counter(1) // 1 counter(2) // 3
In this case, the closure takes no parameters. It infers that the return value is an integer.
This closure's state is explicitly defined within square brackets after the parameter list. The closure's state is a semi-colon-separated collection of named closure variables. Each closure variable specifies its initial value (and implicitly its type). If no initial value is specified, it will use the current value of that same-named variable in the outer scope.
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 specification of the closure's state has several benefits:
- 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, rather than make a copy of a value.
Notice how the closure's logic accesses its state using bound closure variables.
Closures are values
Although closures may be viewed as functions with a persistent state, it is more useful and accurate to view them as a data value that carries an associated function (method). Later pages add important details on additional capabilities 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.