A closure can be thought of as a function that has a state that is preserved from one execution to the next. The closure's logic may retrieve or alter its state using bound closure variables.

Because closures preserve state information, they are useful for:

A closure can be created almost as easily as an anonymous function. The crucial difference is the need to specify the bound closure variables in curly braces just before the parameter list. For example, here we create a simple generator closure:

imm counter = &fn {mut cnt=0}() {cnt += 1}
counter()   // 1
counter()   // 2

Cone's closures are somewhat different than closures in other languages. Cone expects the bound closure variables to be declared, which often improves code readability. Also, closure variables are bound by value, which is more flexible and reduces the likelihood of errors. Closure variables may hold references, if desired.

Closure variable declarations look like those for any variable and are separated by semicolons. Each variable should always specify an initial value. If the name of the closure variable matches the name of a variable in the outer scope holding the desired value, one can specify "name" as a shortcut for "name=name".

Closures are created as references. As such they can be created as lifetime-constrained borrowed references or using regions. Likewise, reference permissions may be specified (though the default 'uni' permission is very useful for moving around ownership of event handlers).

Closure Traits

Let's examine a more complex example: using a closure as an event handler.

window.whenClicked(&fn {window}() { ... })

Since our anonymous closure has no named type, what declared type does the whenClicked method expect? It needs to be a type that can accept a number of different anonymous closures. In this case, the correct answer is to use the standard library's predefined Closure trait. Closure is declared like this:

trait Closure
  fn `()`()

With this definition, Closure is a match for any struct that implements the `()` method with no parameters. Since closures are implemented using structs (more on that below), the Closure trait will then match on any closure whose method has no parameters and returns no values.

We can now declare window.whenClicked as follows:

fn whenClicked(handler &Closure) { .. }

When the event fires on a click, the event handler can now call the closure to perform the desired action, which has access to needed state information:


Closures as structs

Cone's anonymous closure syntax is actually syntactic sugar which (under the covers) builds an anonymous struct and then creates an instance of it. The closure's bound variables become the fields of the struct. The defined method becomes the struct's named `()` method, allowing the struct to be "called" as if it were a function.

Here is the equivalent code generated for the earlier example:

struct Handler
	window Window
	fn `()`() { .. }

imm handler = Handler{window}