Closures

Closures are a powerful piece of code present in many languages. Essentially, a closure wraps code or variables used only within the scope of the current code in a neat little package.

In its simplest form, we can have something like this:

let add = |x : i32 | x + t; 

The part within the | | defines a variable called x that is only used within the scope of the calculation, and it is of type i32.

Okay, that may not seem that useful—after all, what we're doing here is adding two numbers together. Hold on though—if x is only defined within the scope of the calculation, what does x actually equal?

This is where closures come into their own. Typically, when we create a binding, we create a binding to something definite. Here, we are creating a binding, but binding it to the content of the closure. Anything between the pipes (| |) is an argument, with the expression being whatever follows the end pipe.

If you think about it, you've actually created something closer to the following:

fn add(x : i32) -> i32 
{ 
     x + x 
} 

In answer to our question "what does x actually equal?", here it is equal to the only known parameter, t. Therefore, x + t is the same as saying t + t. The add variable isn't being bound directly (that is, in the same way that we bind under normal conditions), but is borrowing the binding. This means that we have to apply the same borrowing rules as before. Say that we have the following:

let m = &mut t; 

This will give the following error:

Figure 7
You will find an example of this error in Chapter 11/close_mut_error.

The important part of the throwback is that we're trying to borrow something that is being borrowed in an immutable line. We can fix this by changing the scope of the closure, as shown here:

let mut t = 10i32; 
{ 
    let add = |x : i32 | x + t; 
} 
let m = &mut t; 

This will result in the error going.

With that in mind, we can start to expand on this. If the value between the pipes is the argument, then we can clearly do some interesting things with closures

The code for this part can be found in Chapter11/closures.

Take this code, for example:

let calc = |x|  
{ 
    let mut result: i32 = x; 
    result *= 4; 
    result += 2; 
    result -= 1; 
    result 
}; 

Rather than create a whole new function, we use the closure and create the function inline with result and x only existing within the scope of the enclosure { }.

A closure without any arguments is the inline equivalent of the following:

fn do_something() -> T { ... } 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset