What are closures in Rust?
Overview
Anonymous functions are known as closures in Rust. We can store them in a variable or a function takes them as a parameter. They behave like a function, but they possess the property of capturing values from the environment in which we define them.
Syntax
Let’s look at the syntax of closures below:
let variable_name = |list of parameters| -> return_type {"body of the closure"};
-
let: This is used to declare any variable in rust. -
variable_name: This is the name of the variable we assign the closure to. -
list of parameters: This is the list of parameters we pass to the closure, which is enclosed in between| |. -
return_type: This contains the return type of values we want to return. -
{}: This contains the body of the closure.
Difference between closures and functions
Property | Closure | Function |
Syntax | Parameters passed between | Parameters passed between |
Body | Enclosed between | Mandatory to enclose between |
Return types | Not mandatory | Mandatory |
Data types | Not mandatory | Mandatory |
Environment capturing | Yes | No |
Code example
We can call closures in the same way as functions, as shown below:
fn main(){let message = " Welcome to educative ";let print_message = || { println!("Message is : {}", message); };print_message();}
Code explanation
The program uses a closure to print a message. It also demonstrates the environment capturing property of closure.
- Line 2: We declare a variable to store the message as a string. Here, we did not explicitly mention the type of the variable, so it is inferred as a
string. - Line 3: We define a closure
print_messagewith no input parameters. The body of the closure contains a print statement that prints the value of the variablemessagedefined out of this closure. This is the environment capturing property of closures.
Call traits in closures
All closures implement the fnOnce trait. With the help of this property, we make sure that we call a closure only once.
A closure can implement a few other call traits depending on how it captures its environment.
- Closures can implement
fnmut, which means we can call them using a mutable reference. This is applicable where the closure has to borrow a variable. - A closure that does not need to borrow a variable in a mutable manner uses
fn.
The move keyword in closures
Whenever a closure uses a variable from its capturing environment, it captures either by reference or by mutable reference.
The move keyword helps capture the closure’s environment by value. It gives them the ownership of the variable to the closure.
Code example
Let’s look at the code below:
fn main() {let prime_numbers = vec![2, 3, 5,7];let closure_variable = move |vec| vec == prime_numbers;println!("Cannot use prime_numbers here : {:?}", prime_numbers);let new_vector = vec![2, 3, 5,7];assert!(closure_variable(new_vector));}
Code explanation
The above code throws an error:
- Line 3: The closure
closure_variabletakes the ownership of the variableprime_numbersusing a move keyword. - Line 4: The
println!function borrows theprime_numbersvariable to print, but the compiler throws an error, as the closure now owns the variable, and themovekeyword moves it in the closure.
Free Resources